Clojure 执行函数直到特定条件保持
我想对某个状态重复应用某个函数,直到某个条件成立为止 函数f接受一个状态,修改它并返回它。再次将f应用于返回的状态,依此类推 我想这会管用的Clojure 执行函数直到特定条件保持,clojure,functional-programming,Clojure,Functional Programming,我想对某个状态重复应用某个函数,直到某个条件成立为止 函数f接受一个状态,修改它并返回它。再次将f应用于返回的状态,依此类推 我想这会管用的 (first (filter pred (iterate f x))) 但是有点难看。加上内存消耗并不理想,因为迭代器将被迫求值并保留中间状态,直到返回pred保持true的状态,此时中间状态应该被垃圾收集 我知道您可以编写一个简单的递归函数: (loop [f x p] (if (p x) x (recur f (f x) p)) (defn do-
(first (filter pred (iterate f x)))
但是有点难看。加上内存消耗并不理想,因为迭代器将被迫求值并保留中间状态,直到返回pred保持true的状态,此时中间状态应该被垃圾收集
我知道您可以编写一个简单的递归函数:
(loop [f x p] (if (p x) x (recur f (f x) p))
(defn do-until [f x p]
(if (p x) x (recur f (f x) p)))
(do-until inc 0 #(> % 10)) ; => 11
但我正在寻找一个核心库函数(或一些函数的组合),它以相同的内存效率做同样的事情 不确定您所说的
迭代器是什么意思-您使用它就像使用迭代器一样,我只想确定这就是您的意思。无论如何,我觉得你的解决方案很好,一点也不难看。内存也不是问题:iterate
可以在方便的时候随意丢弃中间结果,因为您不保留对它们的任何引用,只需以“流”的方式调用filter
。听起来您想要while
宏
用法:(测试和身体时)
在测试表达式为true时重复执行主体。假定
某些副作用将导致测试变为假/零。返回零
在一个稍有不同的用例中,for
宏也支持:when和:while选项
用法:(适用于seq exprs body expr)
列出理解力。获取一个或多个向量
绑定表单/集合表达式对,每个后跟零或多个
修饰符,并生成表达式的惰性求值序列。
集合以嵌套方式进行迭代,最右边最快,
嵌套的coll-expr可以引用在先前版本中创建的绑定
装订形式。支持的修饰符为::let[binding form expr…],
:测试时,:测试时
(取100(代表[x(范围100000000)y(范围1000000):而(
你真正想要的是:
编辑
使用高阶函数实现所需结果的一种方法可能是将函数包装成要被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)
(loop [x initial-value]
(if (pred x) x (recur (f x))))
我不认为有一个核心功能能够准确有效地做到这一点。因此,我将使用循环/重现执行此操作,如下所示:
(trampoline (iterable dec) pos? 10)
(loop [x initial-value]
(if (pred x) x (recur (f x))))
循环/重现非常有效,因为它不需要额外的存储,并且在JVM中实现为一个简单的循环
如果您要经常这样做,那么您可以将模式封装在宏中。我认为您应该使循环成为一个简单的递归函数:
(loop [f x p] (if (p x) x (recur f (f x) p))
(defn do-until [f x p]
(if (p x) x (recur f (f x) p)))
(do-until inc 0 #(> % 10)) ; => 11
顺便去一下怎么样
(first (drop-while (comp not pred) (iterate f x))
是的,我是说迭代。那是个打字错误。但这是有道理的。我运行这个程序是为了让pred总是返回true,并且我不会得到内存不足异常。不确定何时,因为它需要一个副作用。没有想到这一点,但是for会起作用,但是您仍然会使用iterate(首先(对于[z(iterate fx):while(pred z))查看while
的源代码,您会发现它非常类似于您手工编写的代码。请记住,数据在clojure中是不可变的,因此“副作用”在这种情况下,可以简单地更新var x的状态。我同意你的回答,只是它不需要是宏?一个简单的递归函数也可以工作?@dbryne当然-如果你愿意,你也可以将其作为函数。虽然在这种情况下,我想我更喜欢宏-你的运行效率会稍微好一点,我倾向于这样做对于任何感觉像“控制结构”的东西,都更喜欢宏。我同意这是最有效的解决方案。如果不是为了实践,我会这么做。一个优点是,很容易对性能进行推理。如果这是我要经常使用的东西,我会使用一个函数。我认为这种类型的函数可以由JVM内联,所以我不确定您为什么要这样做“我想使用宏。是的,当然,这是一个很好的解决方案。但是在核心库中练习使用高阶函数来抽象递归是很好的。顺便说一句,我称之为apply until。在核心库中有这样的东西会很好。”。