Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Please explain me the following Clojure code

Tags:

clojure

I found the following code (in this blog post that solves the Coin Changer Kata):

(defn change-for [amount]
  (let [denominations [25 10 5 1]
        amounts (reductions #(rem %1 %2) amount denominations)
        coins (map #(int (/ %1 %2)) amounts denominations)]
    (mapcat #(take %1 (repeat %2)) coins denominations)))

The part I find difficult is: (reductions #(rem %1 %2) amount denominations).

As I found out, reductions just calculates the resulting collection incrementally based on some given function. Example: (reductions + [1 2 3]) gives [1 3 6].

1         ; first  element
1 + 2     ; second element
1 + 2 + 3 ; third  element

The next function, rem which calculates the remainder is still extremely simple to understand.

To understand the rest of the code I tried the following:

; first try, to see if this call works
; outside the original code (the change-for function)
(reductions #(rem %1 %2) 17 [10 5 1]) ; --> [17 7 2 0]

; tried to use the reductions which takes only one argument
; notice that 17 is now inside the array
(reductions #(rem %1 %2) [17 10 5 1]) ; --> [17 7 2 0]

; further simplified the expression
(reductions rem [17 10 5 1]) ; --> [17 7 2 0]

The last step was to remove the anonymous function as described in this blog post.

Here, things get confusing (at least for me): rem takes 2 arguments and I don't get it how they are applied when using the array [17 10 5 1]. I tried the following calls:

(rem [17 10 5 1]) ; --> gives error
(rem [17 10 5 1] [17 10 5 1]) ; --> also gives error
(rem 17 10) ; --> works, but how do you use it with collections?

Can someone explain me, how this rem function works with the reductions function?

Another thing I don't quite understand is: how are those percent arguments applied (in #(rem %1 %2))? I mean where do they come from? I tried calling rem in the following way, but I get an error: (#(rem %1 %2) 17 [10 5 1]). There must be something the reductions function is doing behind the scenes to make this work, right?

At first I thought that #(rem %1 %2) was a set. These are declared in a similar way as sets and can be easily misused (by someone just starting out with Clojure):

(type #{1 2 3})   ; --> clojure.lang.PersistentHashSet
(type #(1 2 3))   ; --> user$eval12687$fn__12688

Can someone point me to a site/book/whatever that explains Clojure tricks such as "The Special Form for an Anonymous Function"? Most resources just give the simplest constructs (the ones similar to all the other lisp derivatives) without going into the intricacies of Clojure. I found a site which looks pretty good (and also explains the anonymous functions I mentioned above). Any other such resources?

like image 407
Igor Popov Avatar asked Dec 21 '12 12:12

Igor Popov


1 Answers

This:

(reductions #(rem %1 %2) amount denominations)

is equivalent to this:

(reductions rem amount denominations)

and as you noticed

(reductions function start collection) 

returns a sequence of intermediate results of reducing the collection with function (taking start as the first argument of the first step of reduction). function must take two parameters.

So the result of:

(reductions function start [1 2 3 4 5]) 

is

((function start 1) (function (function start 1) 2) ...) 

The #(rem %1 %2) syntax is just a shorthand for defining anonymous function that takes two parameters (%1 and %2), calls rem on them and returns the result.

It's equivalent to:

(fn [a b] (rem a b))
like image 133
soulcheck Avatar answered Oct 04 '22 09:10

soulcheck