如果匹配条件返回最后一个值,有没有办法在Clojure中打破循环?大多数算法都会在找到结果时返回结果并避免完成整个执行。
假设我有一个100个数字的向量,范围从0到100,我想找到数字10.一旦找到10,我希望执行停止。
比我的例子更简单的情况如下:
(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)
当我找到5时,我怎么能停下来?
我搜索得足够多,而且找不到任何实现方法。我在这里也找到了一个答案,说明我所要求的并不存在于Clojure中,但我发现它有点牵强。即使是这种情况,我可以自己实现这样的东西吗?
(我是Clojure的新手。)
答案 0 :(得分:12)
你几乎做对了。重新格式化代码,我们得到
(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))))
...在哪里 - 为简单起见 - 我已经摆脱了println
,并且希望功能可以返回你的信息。
但是,正如你所注意到的那样,它没有:
(MySearch 0)
;nil
为什么呢?
问题是(recur ...)
在外面 if
。这是做什么的?
(< y 10)
的{{1}}条件,when
和(if ...)
(recur ...)
依次执行,后者的结果
回。 y
为10
,因此when
条件失败,因此when
返回nil
。 让我们在<{1}}:
中移动if
现在,Lo和Behold:
(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)))))
因为我们返回了消息,我们知道该函数确实停止了执行。否则它会继续并返回(MySearch 0)
;"I found it! Now I want to stop executing!"
。
在nil
到位的情况下,该函数将输出消息并立即返回println
,就像它执行时一样。那么 - 至于它是否停止执行,你就不是更聪明了。
顺便说一下,作为the answer you find far-fetched的作者,让我再试一次:
反过来说:
nil
才能继续。 recur
是对正在执行的函数(或recur
)的特殊递归调用:
大多数Lisp系统会自动检测此类调用 - 所谓的尾调用。
所以他们没有或不需要像loop
这样的结构。
话虽如此,Clojure 1.5引入了recur
:reduced
- 类似于reduce的构造。你可以阅读它here。
答案 1 :(得分:4)
你真的不需要break
语句,因为循环在clojure中的工作方式与java中的方式不同。例如,以下内容:
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=>
大致相当于java中的if(x == 5) break;
。
你必须记住,在clojure中loop
本身不会循环,而是设置一个recur
目标。
我建议你浏览一下这个主题的Rick Hickey's videos,以便熟悉这个问题。
编辑:你似乎在我的反应中添加了一些代码,但不用担心,命名函数也是一个重复目标,所以我上面说的所有代码仍然适用:)。这是您的代码以更lisp-y样式重新格式化: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=>