1

这个问题并没有真正解释我想做什么,但我想不出其他任何东西。

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,我的abvars 仍然是空的。我知道 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

4

2 回答 2

3

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

但在你的情况下,你可以简单地从切换doseqfor

(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))
于 2013-07-17T11:14:18.050 回答
1

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

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

呵呵,我只是注意到“减少”可能看起来很混乱,因为它正在构建一些东西,但意思是一个东西的集合被“减少”成一个东西。在这种情况下,我们给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])

在这里,我们使用解构语法来引用我们的两个结果结构 - 只需 [with [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用于从集合构建单个结果。

于 2013-07-18T02:26:13.543 回答