1

我编写了这段代码来嵌套一个函数 n 次,并试图扩展代码来处理测试。一旦测试返回 nil,循环就会停止。输出是一个向量,其中包含测试为真的元素。在这种情况下添加一个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]。

4

2 回答 2

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 中接受 false(除了 nil),您可以更懒惰地编写它:

(defn nester [a inter f test-fn]
  (->> (iterate f a)
       (take (inc inter))
       (drop-while test-fn)
       first))

编辑:以上已回答您最初的问题。既然您已经指定完全改变了问题的含义:

如果要生成函数 f 在值 n 上的所有迭代的向量,并带有谓词 p:

(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]
于 2013-11-22T17:32:10.517 回答
1

为了在一个值上“嵌套”或迭代一个函数,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]
于 2013-11-22T19:34:32.543 回答