0

I'm trying to implement deep-reverse in clojure. If lst is (1 (2 (3 4 5)) (2 3)), it should return ((3 2) ((5 4 3) 2) 1).
This is what I have so far:

defn dRev [lst]
    ( if (= lst ())
        nil
        ( if (list? (first lst))
            ( dRev (first lst) )
            ( concat
                ( dRev (rest lst)) (list (first lst))
            )
        )
   )
)

However, my implementation only works if the nested list is the last element, but the resulted list is also flattened.

For eg: (dRev '(1 2 (3 4)) will return (4 3 2 1).
Otherwise, for eg: (dRev '(1 (2 3) 4)) will return (3 2 1) only.

I hit this brick wall for a while now, and I can't find out the problem with my code.
Can anyone please help me out?

1
  • please look at a proper lisp editor. You should not rely on parens but on a proper indent, like Vim or Emacs lisp plugins provide. I might not be relevant for this problem but it will help you when dealing with more complex lisp patches. Commented Oct 3, 2013 at 12:56

3 Answers 3

7

The other answer gave you the best possible implementation of a deep-reverse in Clojure, because it uses the clojure.walk/postwalk function which generalizes the problem of deep-applying a function to every element of a collection. Here I will instead walk you through the problems of the implementation you posted.

First, the unusual formatting makes it hard to spot what's going on. Here's the same just with fixed formatting:

(defn dRev [lst]
  (if (= lst ())
    nil
    (if (list? (first lst))
        (dRev (first lst))
        (concat (dRev (rest lst))
                (list (first lst))))))

Next, some other small fixes that don't yet fix the behaviour:

  • change the function name to conform to Clojure conventions (hyphenation instead of camel-case),
  • use the usual Clojure default name for collection parameters coll instead of lst,
  • use empty? to check for an empty collection,
  • return () in the default case because we know we want to return a list instead of some other kind of seq,
  • and use coll? instead list? because we can just as well reverse any collection instead of just lists:

(If you really want to reverse only lists and leave all other collections as is, reverse the last change.)

(defn d-rev [coll]
  (if (empty? coll)
    ()
    (if (coll? (first coll))
        (d-rev (first coll))
        (concat (d-rev (rest coll))
                (list (first coll))))))

Now, the formatting fix makes it obvious what's the main problem with your implementation: in your recursive call ((d-rev (first coll)) resp. (dRev (first lst))), you return only the result of that recursion, but you forget to handle the rest of the list. Basically, what you need to do is handle the rest of the collection always the same and only change how you handle the first element based on whether that first element is a list resp. collection or not:

(defn d-rev [coll]
  (if (empty? coll)
    ()
    (concat (d-rev (rest coll))
            (list (if (coll? (first coll))
                    (d-rev (first coll))
                    (first coll))))))

This is a working solution.

It is terribly inefficient though, because the concat completely rebuilds the list for every element. You can get a much better result by using a tail-recursive algorithm which is quite trivial to do (because it's natural for tail-recursion over a sequence to reverse the order of elements):

(defn d-rev [coll]
  (loop [coll coll, acc ()]
    (if (empty? coll)
      acc
      (recur (rest coll)
             (cons (if (coll? (first coll))
                     (d-rev (first coll))
                     (first coll))
                   acc)))))

As one final suggestion, here's a solution that's halfways towards the one from the other answer by also solving the problem on a higher level, but it uses only the core functions reverse and map that applies a function to every element of sequence but doesn't deep-recurse by itself:

(defn deep-reverse [coll]
  (reverse (map #(if (coll? %) (deep-reverse %) %) coll)))
Sign up to request clarification or add additional context in comments.

Comments

5

You can build what you are writing with clojure.walk/postwalk and clojure.core/reverse. This does a depth-first traversal of your tree input and reverses any seq that it finds.

(defn dRev [lst]
  (clojure.walk/postwalk #(if (seq? %) (reverse %) %) lst))

1 Comment

Hi Jared, I've appreciated the help but I'm very new to clojure. Is there any way that I could stick with my current understanding to solve the problem?
0

Here is my version of the problem, if you enter something like this:

(deep-reverse '(a (b c d) 3))

It returns

=> '(3 (d c b) a)

The problem is taken from Ninety-Nine Lisp Problems

My code ended up like this, though, they might be better implementations, this one works fine.

(defn deep-reverse
  "Returns the given list in reverse order. Works with nested lists."
  [lst]
  (cond
    (empty? (rest lst))   lst
    (list? (first lst))   (deep-reverse(cons (deep-reverse (first lst)) (deep-reverse (rest lst))))

    :else
    (concat (deep-reverse (rest lst)) (list (first lst)))))

Hope this is what you were looking for!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.