问题并没有真正解释我想做什么,但我想不出别的什么。
我在一段代码中的外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
,我的a
和b
变量仍然是空的。我认为a和b的范围不会延伸到doseq之外,因为它可以保持从内部进行的任何更改并且它们是不可变的。
在这种情况下,如何计算a和b的值?我尝试将doseq的功能提取到另一个函数中并使用:
调用let(let [a (do-that-function)])
等等,但即便如此,我也无法找到一种方法来跟踪剂量循环中的所有修改,然后作为一个整体发回。
我是以错误的方式接近这个吗?
由于
修改
真的,我要做的是:
(let [a (doseq [x [1 2 3]] {x (* x x)})]
(println a))
但是doseq返回nil所以a将是nil:-s
答案 0 :(得分:3)
clojure中的所有变量都是不可变的。如果您需要可变状态,则应使用atoms或refs。
但在您的情况下,您只需从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))
答案 1 :(得分:1)
诀窍在于考虑添加到现有数据的数据流来制作新数据,而不是改变过去的数据。对于您正在构建数据结构的特定问题,通常使用reduce:
(reduce (fn [result x] (assoc result x (* x x))) {} [1 2 3])
嘿嘿,我只是注意到“减少”可能看起来令人困惑,因为它正在构建一些东西,但意思是一系列东西被“减少”为一件事。在这种情况下,我们给出一个空的map来开始,它会绑定到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])
这里我们使用解构语法来引用我们的两个结果结构 - 只是用[矢量]]来描述数据。请注意,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
用于构建集合中的单个结果。