重复应用一个函数,直到测试不再为真

时间:2013-11-22 17:12:52

标签: clojure clojure-contrib

我写了这段代码来嵌套一个函数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]。

2 个答案:

答案 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。正如您可能想象的那样,takeuser> (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}}