为了在一个值上“嵌套”或迭代一个函数,Clojure 有这个iterate
函数。例如,(iterate inc 2)
可以将其视为无限惰性列表[2, (inc 2), (inc (inc 2)), (inc (inc (inc 2))) ...]
(我使用[]
方括号不是表示“列表”——实际上,它们表示 Clojure 术语中的“向量”——但为了避免与()
哪个可以表示数据的混淆list 或应该是函数调用的 s 表达式 -不iterate
返回向量)。当然,您可能不想要一个无限列表,这就是惰性部分的用武之地。惰性列表只会为您提供所需的内容。因此,如果您要求前十个元素,这就是您得到的:
user> (take 10 (iterate inc 2))
> (2 3 4 5 6 7 8 9 10 11)
当然,你可以尝试询问整个列表,但要准备好重新启动你的 REPL,或者在单独的线程中调度,因为这个调用永远不会结束:
user> (iterate inc 2)
> (2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
=== Shutting down REPL ===
=== Starting new REPL at C:\Users\Omnomnomri\Clojure\user ===
Clojure 1.5.0
user>
在这里,我正在使用clooj,这就是我重新启动 REPL 时的样子。无论如何,这只是一个切线。关键是iterate
回答了你问题的核心。另一部分,在某些测试条件下停止,涉及take-while
。正如您可能想象take-while
的那样,它很像take
,只是不是在一些元素之后停止,而是在某些测试条件下停止(或者用 Clojure 的说法,一个谓词):
user> (take-while #(< % 10) (iterate inc 2))
> (2 3 4 5 6 7 8 9)
请注意,take-while
它的谓词测试是排他的,因此一旦值未通过测试(小于 10),它就会排除该值,并且只在返回结果中包含先前的值。此时,解决您的示例非常简单:
user> (take-while #(< % 7) (iterate inc 2))
> (2 3 4 5 6)
如果你需要它是一个向量,把整个东西包装在一个调用中vec
:
user> (vec (take-while #(< % 7) (iterate inc 2)))
> [2 3 4 5 6]