我想重复将一些函数应用到某个状态,直到条件成立为止。
函数f采用状态,修改它并返回它。再次将f应用于返回状态,依此类推。
我认为这样可行。
(first (filter pred (iterate f x)))
但它有点难看。加上内存消耗并不理想,因为迭代器将被强制评估并保持中间状态,直到返回pred保持为true的状态为止,此时中间状态应该被垃圾收集。
我知道你可以编写一个简单的递归函数:
(loop [f x p] (if (p x) x (recur f (f x) p))
但我正在寻找一个核心库函数(或某些函数组合),它们以相同的内存效率执行相同的操作。
答案 0 :(得分:5)
你真正想要的是take-while:
take-while
function
Usage: (take-while pred coll)
Returns a lazy sequence of successive items from coll while
(pred item) returns true. pred must be free of side-effects.
修改
使用高阶函数来实现所需结果的方法可能是将函数包装成trampoline
要使用的函数,即一个将返回最终结果的函数或另一个将执行的函数下一步。这是代码:
(defn iterable [f] ; wraps your function
(fn step [pred x] ; returns a new function which will accept the predicate
(let [y (f x)] ; calculate the current step result
(if (pred y) ; recursion stop condition
(fn [] (step pred y)) ; then: return a new fn for trampoline, operates on y
y)))) ; else: return a value to exit the trampoline
迭代执行如下:
(trampoline (iterable dec) pos? 10)
答案 1 :(得分:4)
不确定iterator
的含义 - 您使用它就像iterate
一样,我只想确定您的意思。无论如何,你的解决方案看起来很好,而且一点也不丑。内存也不是问题:iterate
可以随时丢弃中间结果,因为你没有对它们进行任何引用,只是以“流”方式调用它上面的filter
。
答案 2 :(得分:3)
我认为你应该让你的循环成为一个简单的递归函数:
(defn do-until [f x p]
(if (p x) x (recur f (f x) p)))
(do-until inc 0 #(> % 10)) ; => 11
答案 3 :(得分:3)
drop-while
怎么样?(first (drop-while (comp not pred) (iterate f x))
答案 4 :(得分:2)
我认为没有一个核心功能可以完全有效地完成这项工作。因此,我会使用loop / recur执行此操作,如下所示:
(loop [x initial-value]
(if (pred x) x (recur (f x))))
Loop / recur非常高效,因为它不需要额外的存储空间,而是作为JVM中的简单循环实现的。
如果你要做很多事情,那么你总是可以将模式封装在一个宏中。
答案 5 :(得分:0)
听起来你想要while
宏。
http://richhickey.github.com/clojure/clojure.core-api.html#clojure.core/while
用法:(测试和身体时) 当测试表达式为真时,重复执行body。假定 一些副作用会导致测试成为假/零。返回nil
在略有不同的用例中,for
宏支持:when和:while选项。
http://richhickey.github.com/clojure/clojure.core-api.html#clojure.core/for
用法:(对于seq-exprs body-expr) 列表理解。采用一个或多个向量 binding-form / collection-expr对,每个对后跟零或更多 修饰符,并产生一个懒惰的expr评估序列。 集合以嵌套方式迭代,最快, 和嵌套的coll-exprs可以引用先前创建的绑定 绑定形式。支持的修饰符是:: let [binding-form expr ...], :测试时,:测试时。
(取100(对于[x(范围100000000)y(范围1000000):while(< y x)] [x y]))