Mücahit Şenol
Mücahit Şenol

Reputation: 97

How to return same list by using reduce function in Clojure?

In DrRacket to return the list without changing by using foldr done this way:

(foldr cons '() '(1 2 3))

However in Clojure reduce is fold left so how can I do this?

First I tried this:

(reduce cons '() '(1 2 3))

=> IllegalArgumentException Don't know how to create ISeq from: java.lang.Long  clojure.lang.RT.seqFrom (RT.java:542)

Afterward I tried this:

(reduce conj '() '(1 2 3))
=> (3 2 1)

"=>" is the output in REPL

No I don't want to read how Clojure implements reduce. I already know that. This is a more specific question. I found the answer myself, I will post it.

Upvotes: 0

Views: 612

Answers (3)

Mücahit Şenol
Mücahit Şenol

Reputation: 97

Here is my solution. I don't know how efficient it. We used this kind of solutions in University during Racket lessons.

(reduce #(concat %1 (list %2)) '() '(1 2 3))
=> (1 2 3)

Upvotes: 0

Nathan Davis
Nathan Davis

Reputation: 5766

I'm not that familiar with Racket, but Clojure's reduce appears to differ from Racket's foldr in two major ways:

  • reduce processes the list from head to tail (vs. tail to head for foldr). In this respect, reduce is similar to foldl.
  • reduce passes the accumulated value as the first argument to the reducing function (vs. the last argument for foldr/foldl).

The second difference is the reason for the error when using cons -- (cons '() 1) is the first call made, and 1 obviously isn't a list.

If we consider that (conj xs x) is equivalent to (cons x xs) when xs is a list, then (reduce conj '() '(1 2 3)) is equivalent to (cons 3 (cons 2 (cons 1 '()))) which might be more apparent when written as

(->> '()
     (cons 1)
     (cons 2)
     (cons 3))

Now, if you don't mind the result being a vector instead of a list, you could do:

(reduce conj [] '(1 2 3))

Or, if you prefer, you could convert the result into a seq so that it essentially behaves like a list:

(seq (reduce conj [] '(1 2 3)))

Alternatively, you could reverse the input list:

(reduce conj () (reverse '(1 2 3)))

Upvotes: 0

Shlomi
Shlomi

Reputation: 4746

In your second attempt you had to "flip" the arguments passed to cons for things to work:

(reduce #(cons %2 %1) '() '(1 2 3))
=> (3 2 1)

However, as you noticed, reduce is actually a fold-left, so the first item in the original list, becomes the inner-most (or last) item in the result list. This could be handled with reverse:

(reduce (fn[a b](cons b a)) '() (reverse '(1 2 3)))
=> (1 2 3)

You can read more about why clojure 'lacks' foldr in here

Upvotes: 3

Related Questions