Clojure 执行函数直到特定条件保持

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-

我想对某个状态重复应用某个函数,直到某个条件成立为止

函数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-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。在核心库中有这样的东西会很好。”。