内部的Clojure循环(全局v局部变量)

时间:2016-02-05 00:04:50

标签: loops clojure global-variables local-variables let

我正在编写与“减少”相同的代码。在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函数也很棒)。

2 个答案:

答案 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进行尾递归,你可以强制/迭代地思考它:

  1. 如果init是提前返回的信号,则返回其值,否则转到第2步
  2. 如果coll为空,则返回init,否则请转到第3步
  3. init设置为使用f调用init并将coll的第一项作为参数
  4. 的结果
  5. coll设置为coll中除第一个
  6. 之外的所有项目的序列
  7. 转到第1步
  8. 实际上,在引擎盖下(具有尾调优化等),这基本上是真正发生的事情。我鼓励你比较同一解决方案的这两个表达式,以便更好地了解如何在Clojure中解决这些问题。