【问题标题】:How to append to variable from within another function in clojure?如何从clojure的另一个函数中附加到变量?
【发布时间】:2013-07-17 11:00:42
【问题描述】:

这个问题并没有真正解释我想做什么,但我想不出别的。

我在一段代码的外部let 函数中有一个空映射,还有一个整数数组。 我想遍历整数数组,执行一个简单的任务,并不断将结果映射附加到外部变量中的变量。

(let [a {}  ;outer variables
      b {}]
  (doseq [x [1 2 3]]
         (let [r (merge a {x (* x x)}) ;I want to append this to a
               s (merge b {x (+ x x)})] ;and this to b
           (println (str "--a--" r "--b--" s)))))

但是一旦我离开doseq,我的ab 变量仍然是空的。我知道 a 和 b 的范围并没有扩展到doseq 之外,因为它可以保留从内部完成的任何更改,并且它们是不可变的。

请问在这种情况下如何计算 a 和 b 的值?我试图将doseq的功能提取到另一个函数中并调用let:

(let [a (do-that-function)])

等等,但即便如此,我还是想不出一种方法来跟踪doseq循环中的所有修改,然后作为一个整体发回。

我是否以错误的方式处理这个问题?

谢谢


编辑

真的,我想做的是:

(let [a (doseq [x [1 2 3]] {x (* x x)})]
  (println a))

但是 doseq 返回 nil 所以 a 将是 nil :-s

【问题讨论】:

  • 作为一个迂腐点,clojure 中没有变量。符号绑定到值。该值不会改变,因此不是可变的(但符号可以在不同的上下文中绑定到不同的值)。 atom 和 refs 之类的东西可以像变量一样起作用。

标签: clojure


【解决方案1】:

clojure 中的所有变量都是不可变的。如果你需要一个可变状态,你应该使用atomsrefs

但在您的情况下,您可以简单地从 doseq 切换到 for

(let [a (for [x [1 2 3]] {x (* x x)})]
  (println a))

这是一个用原子解决问题的示例:

(let [a (atom {})
      b (atom {})]
  (doseq [x [1 2 3]]
    (swap! a assoc x (* x x))
    (swap! b assoc x (+ x x)))
  (println "a:" @a)
  (println "b:" @b))

但你应该尽量避免使用可变状态:

(let [l [1 2 3]
      a (zipmap l (map * l l))
      b (zipmap l (map + l l))]
  (println "a:" a)
  (println "b:" b))

【讨论】:

  • 天哪! for 完成这项工作!也谢谢你的解释!
【解决方案2】:

诀窍在于将数据流添加到现有数据中生成新数据,而不是更改过去的数据。对于正在构建数据结构的特定问题,通常使用 reduce:

(reduce (fn [result x] (assoc result x (* x x))) {} [1 2 3])

呵呵,我刚刚注意到“reduce”可能看起来令人困惑,因为它正在构建某些东西,但其含义是一组事物被“简化”为一个事物。在这种情况下,我们首先给 reduce 一个空映射,它绑定到结果 fn,并且对集合的每个连续映射都会产生一个新结果,我们再次使用 assoc 添加到该结果。

你也可以说:

(into {} (map (fn [x] [x (* x x)]) [1 2 3]))

在您的问题中,您想从一个集合中一次制作多个东西。这是一种方法:

(reduce (fn [[a b] x] [(assoc a x (* x x)) (assoc b x (+ x x))]) [{} {}] [1 2 3])

这里我们使用解构语法来引用我们的两个结果结构 - 只需 [使用 [vectors]] 制作数据图片。请注意,reduce 仍然只返回一个东西——在这种情况下是一个向量。

而且,我们可以概括:

(defn xfn [n fs] 
  (reduce 
    (fn [results x] (map (fn [r f] (assoc r x (f x x))) results fs)) 
      (repeat (count fs) {}) (range n)))

=> (xfn 4 [* + -])
({3 9, 2 4, 1 1, 0 0} {3 6, 2 4, 1 2, 0 0} {3 0, 2 0, 1 0, 0 0})

结果是地图列表。如果您想在构建这些结果时采取中间步骤,您可以将 reduce 更改为 reduce。通常,map 用于转换集合,reduce 用于从集合构建单个结果。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-24
    • 2017-11-04
    • 1970-01-01
    • 2021-02-15
    • 2018-12-08
    • 2015-04-12
    相关资源
    最近更新 更多