user377628
user377628

Reputation:

Counting a sequence in Clojure

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

Answers (5)

Travis S
Travis S

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

om-nom-nom
om-nom-nom

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

sa___
sa___

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

Artem
Artem

Reputation: 784

There is another approach by converting sequence to Java array and calling alength function:

#(alength (to-array %))

Upvotes: 0

Ross Presser
Ross Presser

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

Related Questions