如何在Clojure中正确使用“iterate”和“partial”?

7
大多数关于iterate的引用都是针对操作符,而所有与函数相关的应用都很令人困惑,以至于我仍然不知道如何在我的代码中使用iterate以及什么是partial
我正在完成一个编程作业,尝试使用牛顿法来获取数字n的平方根。也就是说,从猜测为初始近似值开始,通过计算近似值和n/近似值的平均值来计算新的近似值。继续计算,直到最近的两个近似值之间的差异小于epsilon。
我现在正在尝试先完成近似值部分,我相信这是我需要使用iteratepartial的内容。然后,epsilon是我需要使用“take”的东西吗?
这是我目前用于近似值的代码(未包含epsilon):
(defn sqrt [n guess]
  (iterate (partial sqrt n) (/ (+ n (/ n guess)) 2)))

这段代码存在一些问题,当我输入(sqrt 2 2)时,它会给我一个错误:(3/2 user=> ClassCastException clojure.lang.Cons cannot be cast to java.lang.Number clojure.lang.Numbers.divide (Numbers.java:155)

我猜这是需要反复迭代的部分?请问有人能给我一些提示吗?再次强调,这是一道作业题,所以请不要直接提供整个问题的解决方案,我需要一些可以从中学习的思路和解释。


1
什么是“运算符”?我认为在Clojure中没有叫做运算符的东西。在其他语言中,像+-*/这样的东西会被称为运算符,但在Clojure中它们只是函数。你可以在任何需要它们的地方使用任何其他函数(只要它们接受相同的参数)。 - noisesmith
1
大家好,非常感谢你们的帮助!我已经把所有东西都搞清楚了。由于这是一份还未到期的作业,我不认为我可以发布我的代码。 - zaolian
3个回答

6

partial接受一个函数和至少一个该函数的参数,并返回一个新函数,该新函数期望剩余的参数。

(def take-five (partial take 5))
(take-five [1 2 3 4 5 6 7 8 9 10])
;=> (1 2 3 4 5)

iterate通过取两个参数来生成无限序列。一个是函数,另一个是初始值。初始值被用作生成列表中的第一个元素,第二个元素由将函数应用于种子而计算得出,第二个值被用作函数的输入以获得第三个值,以此类推。

(take-five (iterate inc 0))
;=> (0 1 2 3 4)

ClojureDocs提供了有关以下两个函数的良好文档: http://clojuredocs.org/clojure_core/clojure.core/iteratehttp://clojuredocs.org/clojure_core/clojure.core/partial


谢谢你的帮助!我已经解决了这个问题! - zaolian

4
所以,@ponzao 已经很好地解释了iteratepartial的作用,@yonki 指出你实际上不需要它。如果你想探索更多seq函数的话,尝试使用它可能是个好主意(虽然来自惰性序列的开销可能会导致性能不太理想)。
提示:
  • (iterate #(sqrt n %) initial-approximation)将给您一系列近似值。
  • 您可以使用partition创建相邻近似值对。
  • 使用drop-while丢弃不满足epsilon条件的一切。
  • 获取结果。
使用序列求解这个问题可能非常有意义,因为您可以接触到许多有用的seq功能。 注意:此答案的编辑历史记录中可能有完整的解决方案。对此表示抱歉,没有完全理解“作业”的部分。

谢谢你!我已经弄清楚了我的问题的答案! - zaolian

0

我认为你没有理解重点。你既不需要iterate也不需要partial

如果你需要执行一些计算直到条件满足,你可以使用易于理解的loop/recur指令。 loop/recur可以理解为:进行一些计算,检查是否满足条件,如果是,则返回计算值,如果不是,则重复计算。

由于你不想要整个解决方案,只是想要一个建议,那就好好看看loop/recur,一切都会好起来的。

@noisesmith提出了很好的观点。reduce不是用于计算直到条件满足,但在执行有限步骤的某些计算时可能会有用。


1
循环/递归对于不习惯函数式编程的人来说非常有帮助,因为它更接近命令式的做事方式,但通常不是解决问题的最佳方式。而 reduce 不是用于执行某些操作直到条件得到满足,它是用于在集合中进行折叠的(特殊情况下可以使用 reduced 命令式跳出)。 - noisesmith
1
我知道在函数式编程中,循环/递归并不是最好的选择。但正如你所说,它接近于命令式循环,并且在某些人对函数式编程的了解不够深入时可能会有用。而且,我知道reduce的作用。尽管如此,在有限步骤内执行一些计算可能仍然有用。问题不在于Clojure的惯用方式,而在于解决实际问题的可能性。我选择循环/递归是因为它容易理解。 - yonki
1
我认为这是错误的建议。迭代和部分函数是非常有用的工具,比循环/递归更符合习惯用法,而且OP的问题非常适合使用迭代。 - Hendekagon
非常感谢!我完全明白你为什么说服我使用循环/递归。我也同意它们是很好的想法。但是我认为这个家庭作业的重点是帮助我更好地理解迭代/偏函数。但再次感谢你的帮助! - zaolian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接