Mastermind in Clojure and Java
Have you heard of mastermind? It’s a logic game in which one player repeatedly tries determine the secret code that another player set at the beginning. With each guess, the player is given information on how many correct pegs his guess contains, and how many are in the correct position. For more details on the rules, check the wikipedia article.
I built a Clojure implementation of Mastermind as a learning project. (Code and details of functionality at github.) My primary goals were to gain familiarity with the language, and to see what the functional programming paradigm feels like when working on a project that is larger than one big function. After I finished, I re-implemented (almost) all of the features in Java for a comparison. (Code also available at github.) Interested in the results? read on.
Some numbers
Lines of code: Clojure 140, Java 450
Source code files: Clojure 5, Java 11
But it’s really worse than that. I didn’t implement a few features in Java, which code have amounted to another hundred lines. And about 10% of the lines in Clojure were comments, but I didn’t add any comments to the Java code. So I don’t think that it’s unreasonable to expect four times as many lines of code in Java compared to Clojure.
Semantics
Suppose you need to compare two lists, and count the number of pegs that are the same, and in the same place. This is necessary to determine the number of colored pegs given in response to a guess in Mastermind. Here’s how you do it in Java:
private static int exactMatches(List<Pre> guess, List<Pre> actual) { int matches = 0; for (int idx = 0; idx < actual.size(); idx++) { if (actual.get(idx) == guess.get(idx)) { matches++; } } return matches; }
Not too bad, right? Just a loop, with a conditional that increments a counter. But notice that since we are comparing two lists, it is necessary to use the indices. That doesn’t seem like a big deal, because it is common, but understand that this question being asked has nothing to do with indices. Let’s compare this with the Clojure implementation.
(defn exact-matches [guess actual] (count (filter true? (map = guess actual))))
In my mind at least, this function is not only more succinct, but far closer to my mental model of what is happening. There is no counter, only a count
function. There is no loop containing a conditional, rather a filter
function that takes a function and a sequence. For the comparison, map
applies the equals function successively to each of the items in the two sequences, producing a boolean
sequence. Feel free to browse the code for more Java/Clojure comparisons.
Performance
Unfortunately, Java clearly won regarding performance. I’m not going to include any numbers here, because benchmarking should be done with simpler algorithms, where it is more obviously a fair comparison. Nevertheless, I won’t be recommending Clojure for any performance intensive applications soon.
Conclusion
For algorithmic problems, the flexibility and functional semantics are a huge win for Clojure. The performance is a negative. I didn’t miss Java’s static typing either, but that’s unfair, given how small this project was. As for objects, I haven’t missed them so far, and I’m loving the ease of use of Clojure collections. Time for another Clojure project, this time something that makes use of Clojure’s concurrency language features.
[…] Mastermind in Clojure and Java avendo tempo… ::: Code and comments […]
Visto nel Web – 57 « Ok, panico
December 16, 2012 at 1:28 am