我写了这段代码来嵌套一个函数n次,我试图扩展代码来处理测试。一旦测试返回nil,循环就会停止。输出是包含测试为true的元素的向量。在这种情况下添加while循环是否最简单?这是我写的一个例子:
(defn nester [a inter f]
(loop [level inter expr a]
(if (= level 0) expr
(if (> level 0) (recur (dec level) (f expr))))))
示例输入是整数2,我想嵌套inc函数,直到输出大于6.输出应为[2 3 4 5 6 7]。
答案 0 :(得分:3)
(defn nester [a inter f test-fn]
(loop [level inter
expr a]
(if (or (zero? level)
(nil? (test-fn expr)))
expr
(recur (dec level)
(f expr)))))
如果你也接受了test-fn的假(除了nil),你可以更加懒惰地写这个:
(defn nester [a inter f test-fn]
(->> (iterate f a)
(take (inc inter))
(drop-while test-fn)
first))
编辑:以上内容已回答您的初步问题。现在您已经指定完全改变了您的问题的含义:
如果要生成函数f的所有迭代的向量,而不是带有谓词p的值n:
(defn nester [f n p]
(->> (iterate f n)
(take-while p)
vec))
(nester inc 2 (partial > 8)) ;; predicate "until the output is greater than six"
;; translated to "as long as 8 is greater than
;; the output"
=> [2 3 4 5 6 7]
答案 1 :(得分:1)
要在函数上“嵌套”或迭代函数,Clojure具有iterate
函数。例如,(iterate inc 2)
可以被视为无限懒惰列表[2, (inc 2), (inc (inc 2)), (inc (inc (inc 2))) ...]
(我使用[]
括号不表示“列表” - 实际上,它们代表“向量”在Clojure术语中 - 但为了避免与()
混淆,iterate
可以表示数据列表或应该是函数调用的s表达式 - user> (take 10 (iterate inc 2))
> (2 3 4 5 6 7 8 9 10 11)
不返回一个矢量)。当然,你可能不想要一个无限列表,这是懒惰部分的用武之地。懒惰列表只会给你你所要求的内容。因此,如果你要求前十个元素,那就是你得到的:
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>
当然,你可以尝试来询问整个列表,但是要准备重新启动你的REPL,或者在一个单独的线程中调度,因为这个调用永远不会结束:
iterate
在这里,我正在使用clooj,这就是我重新启动REPL时的样子。无论如何,这只是一个切线。关键是take-while
回答了问题的核心。另一部分,在一些测试条件下停止,涉及take-while
。正如您可能想象的那样,take
与user> (take-while #(< % 10) (iterate inc 2))
> (2 3 4 5 6 7 8 9)
非常相似,只是在一些元素之后停止,而是在一些测试条件下停止(或者在Clojure用语中作为谓词):
take-while
请注意,user> (take-while #(< % 7) (iterate inc 2))
> (2 3 4 5 6)
与谓词测试是唯一的,因此,一旦值未通过测试(小于10),它将排除该值,并且仅包括返回结果中的先前值。在这一点上,解决你的例子是非常直接的:
vec
如果你需要它作为一个向量,请将整个内容包裹在user> (vec (take-while #(< % 7) (iterate inc 2)))
> [2 3 4 5 6]
的调用中:
{{1}}