Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing break in Clojure

Tags:

clojure

Is there a way to break out of a loop in Clojure if a condition is matched returning the last value? Most algorithms benefit from returning a result when they find it and avoiding completing the whole execution.

Let's say that I have a vector of 100 numbers ranging from 0 to 100 and I want to find the number 10. Once 10 is found I want the execution to stop.

An even simpler case than my example is the following:

(defn MySearch
    [y]
    (when (< y 10)

;;Corrected. Thanks to dsm who pointed it out. Previously was (< y 5).

        (if (= (nth [1 2 3 4 5 6 7 8 9 10] y) 10)
            (println 
                "I found it! Now I want to stop executing!"
            )
        )
        (recur
            (inc y)
        )
    )
)

(MySearch 0)

How can I stop when I find 5?

I have searched enough and I can't find any way of implementing this. I have also found an answer here that states that what I am asking doesn't exist in Clojure but I find it a little far-fetched. Even if this is the case can I implement something like that myself?

(I am new to Clojure.)

like image 423
Adam Avatar asked Nov 24 '14 04:11

Adam


1 Answers

You nearly got it right. Reformatting your code, we get

(defn MySearch [y]
  (when (< y 10)
    (if (= (nth [1 2 3 4 5 6 7 8 9 10] y) 10)
      "I found it! Now I want to stop executing!")
    (recur (inc y))))

... where - for simplicity - I've got rid of the println, and had the function hopefully return your message.

But, as you've noticed, it doesn't:

(MySearch 0)
;nil

Why?

The trouble is that the (recur ...) is outside the if. What does this do?

  • If the (< y 10) condition for the when is met, the (if ...) and the (recur ...) are executed in turn, and the result of the latter returned.
  • Eventually, y is 10, so the when condition fails, so the when returns nil.

Let's move the recur inside the if:

(defn MySearch [y]
  (when (< y 10)
    (if (= (nth [1 2 3 4 5 6 7 8 9 10] y) 10)
      "I found it! Now I want to stop executing!"
      (recur (inc y)))))

Now, Lo and Behold:

(MySearch 0)
;"I found it! Now I want to stop executing!"

Because we returned the message, we know that the function did stop executing. Otherwise it would have gone on and returned nil.

With the println in place, the function would output the message and return nil immediately, as it would do had it gone on executing. So - as to whether it stopped executing, you're none the wiser.


By the way, as the author of the answer you find far-fetched, let me try again:

  • There is no break statement in Clojure.
  • It's the other way round:

    • You break out of a loop by default.
    • You have to use recur to continue it.
  • recur is a special recursive call to the function (or loop) you are executing:

    • one where it is the returned value.
    • It is said to be in tail position.

Most Lisp systems detect such calls - so called tail calls - automatically. So they don't have or need a construct like recur.


Having said that, Clojure 1.5 introduced reduced: a break-like construct for reduce. You can read about it here.

like image 120
Thumbnail Avatar answered Oct 21 '22 12:10

Thumbnail