Reputation:
I'm solving Problem 22 from the 4Clojure site asking me to write a function that counts the elements in a sequence. Because I messed around with Haskell at one point, I know that using fold
is probably the way to go about doing that. After reading about it, I've come to the understanding that I should be using reduce
for the same purpose. Here's the answer I submitted:
#(reduce inc 0 %)
The reasoning behind this is to iterate the list, and call inc
each time on the value which is initially 0. However, this does not work. The site is complaining that the "Wrong number of args (2) passed to: core$inc". So I tried adding parens around inc
:
#(reduce (inc) 0 %)
Now it thinks zero arguments are being passed to inc
. What am I doing wrong here?
Upvotes: 1
Views: 4998
Reputation: 176
I have been going through the problem list learning clojure too. Came up with this answer to that problem:
reduce + (#(for [o %] 1))
Upvotes: 1
Reputation: 62835
So I tried adding parens around inc...
Please, don't use parens in clojure ever for the purpose of guiding the compiler/interpretator. In clojure every parenthesis counts and this way you're just calling a function with zero arguments.
Now, take a look at the clojure doc on reduce
If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc.
So when you write
(reduce inc 0 [1 2 3])
what actually happens is
(inc
(inc
(inc 0 1) 2) 3)
Proper function might look like
#(reduce
(fn [c _] (inc c))
0 %)
Upvotes: 10
Reputation: 363
Clojure's reduce
can be thought as similar to haskell's left-fold function foldl
.
The haskell way of counting items in a list xs
would be something like this
foldl (const . (+1)) 0 xs
which is better understood as foldl (\acc _ -> acc + 1) 0 xs
.
The idea is to increment the first operand in the fold function and so i'd be tempted to write it as
(reduce #(inc (%1)) 0 xs)
except that is wrong as the arity of the anonymous function is determined by the highest argument referenced in the expression and our fold function has to have an arity of 2.
So, a clever workaround:
(reduce #(inc (first %&)) 0 xs)
Upvotes: 2
Reputation: 784
There is another approach by converting sequence to Java array and calling alength function:
#(alength (to-array %))
Upvotes: 0
Reputation: 6255
Assume the list is
[1 2 3 4]
In your first example, you are telling reduce to execute these:
(inc 0 1)
(inc 0 2)
(inc 0 3)
(inc 0 4)
Clearly inc is being passed two arguments here, when it requires one.
In your second attempt, you are telling reduce that (inc) will return a function which is then to be used with the value and the list. (inc) is a call to the inc function with zero arguments.
What you need for the first argument to reduce is a function that takes two values and returns the first value incremented by one.
Upvotes: 3