Crafting Code in Clojure
tapestryjava.blogspot.se
tapestryjava.blogspot.se
1.) how do I live without a loop that lets me iterate some counters? The first Clojure (sort of) looping structure that I understood was (reduce), so for awhile I jumping through hoops to use (reduce) every time I wanted to go into a loop.
2.) how do I realize lazy sequences? I got myself badly stuck with some of the libraries, for instance Enlive, when I saved the nodes as a string and then later re-imported them to my code, at which point they were lazy sequences and I was wondering, how do I get this back to being Enlive nodes? Took me awhile.
3.) when do I divide code into a new file/namespace. In a language like Java the rule is something like "one class = one file" (I could qualify that, etc...). Clojure doesn't impose obvious dividing points on your code, and I think it takes a while to figure out what the right dividing points are (and I still wonder about what the best practice is, for instance: is it acceptable for code in one namespace to rely upon, and modify, an atom in a different namespace?)
Learning Clojure has been a fantastic educational experience for me. It is my favorite language and my favorite development eco-system. And I am lucky enough to be able to use at my job, so I get to work with it full time now, for which I am very grateful. (Previously I was getting a lot of corporate gigs where they needed PHP programmers who knew the Symfony framework -- I found those jobs very tedious, and, more to the point, after a few years I felt like I was not learning anything new).
I was mostly using Ruby with Rails or Sinatra for a long time, and the switch to Clojure has been really good. Caveat: I have been actively using Lisp since 1982, and even so it took me a little while to switch over from Common Lisp. Keep at it, and I think Clojure will expand the way you think about programming.
import com.google.common.base.Functions;
import com.google.common.base.Joiner;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Ordering;
import com.google.common.collect.Sets;
import java.util.Map;
import java.util.Collection;
public class MapUtils {
public static String sortedKeyList(Map<?, ?> map1, Map<?, ?> map2) {
Collection<String> sortedKeys = FluentIterable
.from(Sets.union(map1.keySet(), map2.keySet()))
.transform(Functions.toStringFunction())
.toSortedSet(Ordering.natural());
return sortedKeys.isEmpty()
? "<none>"
: Joiner.on(", ").join(sortedKeys);
}
}
Not nearly as pretty as the Clojure code, but a lot nicer than without Guava.I personally find the "code compactness" to be a bit of a red herring. Java's greatest weakness, after all, is not the verbosity per se. If all of its other failings were addressed I would probably scarcely care about the amount of code it takes to perform a given task.
Likewise, compactness is not what lies behind the current FP trend. Compactness, after all, while related to programming paradigms is more directly tied to the strength and level of abstraction afforded by a given library / API. Ruby's standard library, for instance, allows for some pretty insane one liners.
This is to say nothing of the development time overhead involved in switching paradigms (non-trivial, IMHO). This alone offsets "compactness", both from the standpoint of up front dev time as well as readability / correctness, for (IMO) up to half a year.
As an aside, that code could be made yet more compact if written in Groovy (Groovy-as-a-Java-superset and not functional Groovy, necessarily).
Indeed. It's very hard to read this and not think ... "but this would just be" in Groovy:
[m1,m2]*.keySet().sum().sort().join(",")?:"<none>"
So small that I would rarely even make a separate function for it. I imagine it's much the same in many dynamic languages.A lot of people are talking about a 10x code size reduction, which is amazing.
Also, it's not as if the Clojure code was some obfuscated brainf*ck-style unreadable one-liners. It's very easy to read for anyone remotely familiar with Clojure.
', '.join(sorted(str(x) for x in set(map1) | set(map2))) or '<none>'
This works by making sets of the keys of map1 and map2, taking the union of those sets, converting each key to a string, sorting those strings, and joining them with ', '. And then if that generated an empty string -- if both dicts were empty -- then it evaluates to '<none>'.Am I missing some difficulty here?
EDIT: and for an arbitrary number of maps, this one works:
import itertools
', '.join(sorted({str(x) for x in itertools.chain(*maps)})) or '<none>'
It iterates over all the keys of all the maps with itertools.chain(), converts everything to a string, adds everything to a set, sorts them, and then joins them with commas. I realize it's not a one-liner anymore because of the itertools import, but it's still pretty simple to follow.I like Clojure, but for problems like this, it just doesn't seem to be doing quite as well as languages like Python.
From the OP:
(->>
(set (concat (keys map1) (keys map2)))
(map str)
sort)
And also from the OP: (sort (map str (set (concat (keys map1) (keys map2)))))
I'm so used to reading Lisp "in to out" that I have a difficult using the thread-fast macro. I guess it depends on your background. Do those who have have a bit of Lisp experience agree with me? (sort (map str
(set (concat (keys map1)
(keys map2))))) def f[T](a:Map[String,T],b:Map[String,T]) = {
val res= (a.keys++b.keys).toList.sorted.mkString(",")
if (res.length == 0) None else Some(res)
}
https://gist.github.com/4694911 (defn f [a b]
(let [res (join "," (sort (distinct (mapcat keys [a b]))))]
(if (empty? res) "<none>" res)))
Also I'm not a Scala expert, but I think you want .keySet instead of .keys, otherwise they won't be unique, right? scala> val p = Map(1->4,5->3)
scala> p.keys
res0: Iterable[Int] = Set(1, 5)
scala> p.keySet
res1: scala.collection.immutable.Set[Int] = Set(1, 5)
scala> p.keys++p.keys
res2: Iterable[Int] = Set(1, 5)
scala> p.keySet++p.keySet
res3: scala.collection.immutable.Set[Int] = Set(1, 5) (defun example (&rest maps)
(format nil "~:[<none>~;~:*~{~A~^, ~}~]"
(sort (remove-duplicates
(loop for map in maps nconc
(loop for key being the hash-key of map collect key))
:test 'equal)
'string<))) go :: (Show k, Eq k) => Map k v -> Map k v -> String
go m1 m2 = map show >>> sort >>> intersperse ", " >>> mconcat >>> orNone
$ keys m1 ++ keys m2
where orNone [] = "<none>"
orNone e = eGratuitious pointfree version (imports omitted):
go :: (Show k, Eq k) => Map k v -> Map k v -> String
go = orNone . intercalate ", " . sort . map show .: (++) `on` keys
where
orNone [] = "<none>"
orNone e = e
f .: g = (f .) . g
infixr 8 .: go :: (Show k, Ord k) => Map k v -> Map k v -> String
go m1 m2 = orNone . intercalate ", " . fmap show . keys $ m1 <> m2
where orNone "" = "<none>"
orNone x = xhttps://github.com/fcostin/crafting_code_in_bf
Writing things in brainfuck is a good way to appreciate the things we sometimes take for granted, e.g. languages with more than 1 pointer.
p ->(*_) {
_.reduce({}, :merge).tap { |_| return "<none>" if _.empty? }.keys.uniq.map(&:to_s).sort.join(", ")
}.({c:3,d:4,b:2}, {f:5,e:4,a:1})
p ->(*_) {
_.reduce({}, :merge).tap { |_| return "<none>" if _.empty? }.keys.uniq.map(&:to_s).sort.join(", ")
}.({}, {})
# "a, b, c, d, e, f"
# "<none>"
edit: a little bit better. func = ->(*_) {
return "<none>" if _.all?(&:empty?)
_.flat_map(&:keys).uniq.map(&:to_s).sort.join(", ")
}
func.({c:3,d:4,b:2}, {f:5,e:4,a:1})
#=> "a, b, c, d, e, f"
func.({}, {})
#=> "<none>"
https://gist.github.com/66b45e765a2aa6a97143 def unique_keys(*hashes)
result = {}
hashes.each do |hash|
result.merge! hash
end
return result.keys.map(&:to_s).sort.join(', ') unless result.empty?
'<none>'
end
https://gist.github.com/4695388I reckon what makes his first example somewhat unidiomatic is the arbitrary lambda and the use & inner return from the #taps block.
His updated example is just good ol tacit Ruby. But then again the amount of times I see an #each block with inner logic used instead of a simple point-free one-liner in Ruby really just proves your point.
(map1.keys | map2.keys).map(&:to_s).sort.join(", ").instance_eval { empty? ? "<none>" : self }a = {"fred": 4, "wilma": 8} b = {"t-shirt": 6, "fred": 0}
print ", ".join(sorted(set(a).union(b))) or "<none>"
>>> a = {'a':1,'b':2}
>>> b = {'b':3,'c':4}
>>> def keylist(*maps):
... return ', '.join(sorted({str(k) for m in maps for k in m.keys()})) or '<none>'
...
>>> keylist(a, b)
'a, b, c'
>>> keylist()
'<none>'
I'm sure people will have differing opinions on the readability of a nested set comprehension, but from the bullets at the top of the article to one-liner implementation in ~30 seconds. (For an arbitrary number of maps, no less.)(edit: added '<none>' on empty, fixed formatting, whoops now I'm over 30 seconds :))
for k in m
be more "pythonic" than for k in m.keys()
? string SortedKeyList<T, U>(params IDictionary<T, U>[] maps)
{
var keys = new SortedSet<string>(maps.SelectMany(m => m.Keys)
.Select(k => k.ToString()));
return keys.Any() ? String.Join(", ", keys) : "<none>";
}
EDIT: I just realized that a lot of curly brace languages look the same to the uninitiated. The above is C#. def sorted_key_list(maps)
maps.map(&:keys)
.reduce(:&)
.sort
.tap { |a| a.push "<none>" if a.empty? }
.join ","
end
Works out at 24 syntax-related punctuation symbols for Ruby, 20 for Clojure (ignoring the replace-empty function, admittedly). def sorted_key_list(maps)
maps.flat_map(&:keys)
.uniq
.sort
.join(",").gsub /^$/, "<none>"
end