我正在编写与“减少”相同的代码。在clojure中的功能 ex)(减少+ [1 2 3 4])=(+(+(+ 1 2)3)4)。
(defn new-reduce [fn coll]
(def answer (get coll 0))
(loop [i 1]
(when (< i (count coll))
(def answer (fn answer (get coll i)))
(recur (inc i))))
answer)
在我的代码中,我使用了全局变量,对我而言,我更容易理解这种方式。显然,人们说最好将全局变量更改为局部变量,例如let。所以我试过..
(defn new-reduce [fn coll]
(let [answer (get coll 0)]
(loop [i 1]
(when (< i (count coll))
(fn answer (get coll i))
(recur (inc i))))))
老实说,我对let函数并不熟悉,即使我尝试了非常简单的代码,但它没有用。有人可以帮我修复这段代码并帮助我理解let(局部变量)是如何工作的吗?谢谢。 (p.s.非常简单的代码,里面有循环let函数也很棒)。
答案 0 :(得分:1)
让我们不创建本地&#34;变量&#34;,它给出了值的名称,并且在给它们命名之后不允许你更改它们。因此引入let更像是定义局部常量。
首先,我只是在loop
表达式中添加另一个项目来存储到目前为止的值。每次循环我们都会更新它以包含新信息。这种模式很常见。我还需要在函数中添加一个新参数来保持初始状态(减少概念需要这个)
user> (defn new-reduce [function initial-value coll]
(loop [i 0
answer-so-far initial-value]
(if (< i (count coll))
(recur (inc i) (function answer-so-far (get coll i)))
answer-so-far)))
user> (new-reduce + 0 [1 2 3])
6
这会移动&#34;全局变量&#34;在跳回到顶部时,每个循环可以更新一个循环表达式本地的名称。一旦到达循环结束,它将返回到目前为止的函数的返回值,而不是再次重复。构建自己的reduce函数是了解如何有效使用reduce的一种很好的方法。
有一个函数引入了真正的局部变量,尽管它几乎从未在Clojure代码中使用过。它只在运行时bootstap代码中真正使用过。如果你真的很好奇,请仔细阅读有关绑定的内容。
答案 1 :(得分:0)
这是一个简单,实用的解决方案,可以复制标准reduce
的行为:
(defn reduce
([f [head & tail :as coll]]
(if (empty? coll)
(f)
(reduce f head tail)))
([f init [head & tail :as coll]]
(cond
(reduced? init) @init
(empty? coll) init
:else (recur f (f init head) tail))))
这里没有loop
,因为函数本身就是递归点。我个人觉得更容易以递归的方式思考这个问题,但是由于我们使用recur
进行尾递归,你可以强制/迭代地思考它:
init
是提前返回的信号,则返回其值,否则转到第2步coll
为空,则返回init
,否则请转到第3步init
设置为使用f
调用init
并将coll
的第一项作为参数coll
设置为coll
中除第一个实际上,在引擎盖下(具有尾调优化等),这基本上是真正发生的事情。我鼓励你比较同一解决方案的这两个表达式,以便更好地了解如何在Clojure中解决这些问题。