2

Im dealing with recursion in clojure, which i dont really understand. I made a small program taked from here that tries to find the smalles number that can be divided by all the numbers from 1 to 20. This is the code i wrotte, but there must be something im missing because it does not work. Could you give me a hand? thanks!

(defn smallest [nume index]
(while(not ( = index 0))
    (do
        (cond
            (zero?(mod nume index))(let [dec' index] (smallest nume index))
            :else (let [inc' nume] (smallest nume index))))))

EDIT: Looks like is better loop/recur so i tried it:

(loop [nume 20
            index 20]
        (if (= index 0)
        (println nume)
        (if (zero?(mod nume index))
                (recur nume (dec index))
                (recur (inc nume) 20)))))

Working. If you are curious about result--> 232792560

5
  • Invest in understanding loop/recur, immutability and the standard libraries sequence transformation functions - while is an imperative looping construct which in my experience one never uses more than once or twice a year. Commented Nov 10, 2016 at 20:08
  • It seem you are shooting in the dark.. For instance, what do you think (index 20) does? Also, in recur, the order of the values matters, I am not sure you are doing what you think you are. Commented Nov 10, 2016 at 20:32
  • 1
    I just edited the problem about order. What i mean with (index 20) is to set index to 20 again. @shlomi Commented Nov 10, 2016 at 20:34
  • @Capie, so that is not what this is doing. In fact, it will throw an error, because index is not a function, and you are attempting to apply 20 to the function index. If you want to keep index as 20, you should simply do (recur (inc nume) 20). This comment also applies to (nume), you are trying to invoke the number :). I updated my answer, see if that helps a little more. Commented Nov 10, 2016 at 20:37
  • 1
    @shlomi Helped a lot. It´s working. Thanks mate. Commented Nov 10, 2016 at 20:43

2 Answers 2

1

while does not do what you think it does.

In clojure, everything (well, almost) is immutable, meaning that if index is 0, it will always be 0 in the same context. Thus looping until it is 1 makes little sense.

There are a number of ways to achieve what you are trying to do, the first, and most trivial (I think!) to newcomers, is to understand loop/recur. So for example:

(loop [counter 0] 
  (when (< counter 10) 
    (println counter)
    (recur (inc counter))))

In here, counter is defined to be 0, and it never changes in the usual way. When you hit recur, you submit a new value, in this case the increment of the previous counter, into a brand new iteration starting at loop, only now counter will be bound to 1.

Edit: Notice however, that this example will always return nil. It is only used for the side effect of println. Why does it return nil? Because in the last iteration, the when clause will return nil. If you want to return something else, you should perhaps use if and specify what would you like to be returned at the last iteration.

You should read a little more about this paradigm, and perhaps do exercises like 4clojure to get a better grasp at this. Once you do, it will become MUCH simpler for you to think in this way, and the tremendous benefits of this style will begin to emerge.

Good luck!

Sign up to request clarification or add additional context in comments.

Comments

0

Here's a brute force implementation testing all numbers on the condition if they can be divided by all numbers from 1 to 10 (please note (range 1 11)) in the code:

(first 
  (filter #(second %) 
          (map (fn[x] [x (every? identity 
                                  (map #(=  0 (mod x %)) 
                                  (range 2 11)))]) 
               (range 1 Integer/MAX_VALUE))))

It's output is

[2520 true]

Unfortunately, this is not a good approach for bigger numbers. With (range 1 21) it doesn't finish after few minutes of waiting on my Macbook. Let's try this:

user=> (defn gcd [a b] (if (zero? b) a (recur b (mod a b))))
#'user/gcd
user=> (reduce (fn[acc n] (if (not= 0 (mod acc n)) (* acc (/ n (gcd n acc))) acc)) 1 (range 1 11))
2520    
user=> (reduce (fn[acc n] (if (not= 0 (mod acc n)) (* acc (/ n (gcd n acc))) acc)) 1 (range 1 21))
232792560

1 Comment

#(second %) is identical to simply second :)

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.