Implementing break in Clojure

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.


You don't really need a break statement, because looping works very differently in clojure than it does in, say, java. For instance, the following:

user=> (loop [[x & t] [0 1 2 3 4 5 6 7 8 9]]
  #_=>   (println "x=" x)
  #_=>   (if (= x 5)
  #_=>     x
  #_=>     (recur t)))
x= 0
x= 1
x= 2
x= 3
x= 4
x= 5
5
user=> 

is roughly equivalent to if(x == 5) break; in java.

You have have to remember that in clojure loop does not, by itself, loop, but rather sets a recur target.

I'd recommend you go through Rick Hickey's videos on the subject in order to get familiar with the weirder aspects of this.

EDIT: You seem to have added some code while I was embettering my response, but not to worry, a named function is also a recur target, so all I said above still applies :). Here is your code reformatted in a more lisp-y style:

user=> ; FYI: This function will never print "success"
user=> (defn my-search
  #_=>   [y]
  #_=>   (if (< y 5) ; <-- Because of this.
  #_=>     (if (= (nth [1 2 3 4 5 6 7 8 9 10] y) 10)
  #_=>       (println "Success!")
  #_=>       (recur (+ y 1)))
  #_=>     (println "y is >= 5")))
#'user/my-search
user=> (my-search 3)
y is >= 5
nil
user=> 

Tags:

Clojure