I completed all of the 4clojure problems!
You can see most of my solutions in the github repo I used for this project. I only committed problems to the repo that I needed to develop inside the editor. Some problems were simple enough to be solved on the website. The ones in the repo should be the more interesting problems.
I’d like to share some of my favorite problems and solutions. Warning: these are spoilers for 4clojure, of course. If you’re interested in learning clojure I’d highly recommend not reading this post and attempting the problems without looking at any answers. The problems start with the basics of the language and build up to substantive and challenging coding exercises in their own right. Each problem is presented with a set of unit tests. Once you solve a problem you can see everyone else’s solutions. This is great for benchmarking yourself and picking up other people’s tricks.
This was my first adventure into clojure and I found the 4clojure problems great for motivating me through the initial learning curve, perhaps a bit past it. Of course, after 4 weeks I’m definitely still a beginner with clojure so I welcome any feedback!
The classic Fibonacci series problem. A goto for learning any language
especially a functional language. This was the first problem I had to write down
because I kept tripping myself with how
conj adds to the beginning of lists. This
frustration is definitely apparent in my submitted solution.
This approach works but is kludgy and far from efficient. Knowing what I know now I’d opt for using a lazy sequence.
Another good approach would be using
recur because clojure doesn’t
tail optimize unless
recur is used.
Replicate a sequence
While I came up with the alternative fibonacci solutions above many of the alternatives I saved with my solutions were other user’s solutions. For example, let’s take problem 33: repeating each element in a series. This was my solution:
Insights like this from other users’ solutions were tremendously useful in learning different parts of the clojure API’s very quickly.
Another place that I got great feedback was the clojure source code. I was introduced to clojure code by the 4clojure problems that consist of reimplementing core functions. The clojure docs link to the relevant line in the source. I really appreciate when documentation links to the source it’s describing.
https://clojuredocs.org/clojure.core/reductions See the link to source in the top right under clojure.core.
I found the clojure source very readable and approachable. Once I started reading the source for the core function problems I started referencing it for other problems as well.
I didn’t learn anything in particular from this problem. I just love this method of testing primality.
Naming the solution function
__ double underscores was a trick that I picked
up to make copying and pasting the testcases from 4clojure quicker.
I used a vim mapping for adding the
asserts. The vim set up for clojure
should probably be a post to itself because that took a minute.
Language of a DFA
The problems on 4clojure are divided into four tiers of difficulty: elementary, easy, medium, and hard. Elementary and easy often could be solved on the website sometimes in one line or one word. The medium and many of the hard were great for making me think a little deeper about the language and use it a more throughly in a substantive problem. The last ten or so of the hard category, however, were a different kind of beast entirely. While I was able to solve the first 140 problems in the first three weeks, the last dozen or so problems took up a disportionate amount of time. I really liked them. They were legitimately difficult.
One of my favorite problems from this group asks to produce the language of deterministic finite automaton (DFA). The class that introduced me to formal languages and their machines was one of my favorite in college, so I’m always excited to see something like a DFA come up. I also like this problem because it makes use of lazy execution which is readily available in clojure.
At each step in the recursion I have passed down a sequence of substring objects
trails, which is used as a queue. For the first substring out of this queue, I
enqueue all the states to which the DFA could transition from that substring’s
state. Finally, before recurring I check if the current substring is accepted by
the DFA by simply checking its’ state. If so, I add it to the lazy sequence.
Either way, I recurse with the new queue of substrings and states
While the problem wasn’t the hardest of the hard (see 140, 152, or 127) it’s a great example of the progress of my clojure writing at the end of these four weeks. I really enjoy several aspects of the language which are used here:
- hashmap objects
- keywords as getter functions from hashmap objects
- argument destructuring
- the let macro’s first-to-last evaluation
- and as I already mentioned availablity of lazy evaluation functions
I had a lot of fun going through 4clojure and I am very thankful to the creators and maintainers. I look forward to more fun with clojure in the future.