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 thewhen
is met, the(if ...)
and the(recur ...)
are executed in turn, and the result of the latter returned. - Eventually,
y
is10
, so thewhen
condition fails, so thewhen
returnsnil
.
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 (orloop
) 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=>