2

我有两个要比较的序列,我需要将比较结果保存在地图中,第一个序列中的数据用作键,第二个用作 val。这是有效的示例代码

(def myAtom  (atom {}))

(map #(if (== %1 %2) (swap! myAtom assoc %1 %2 ))
              [1 2 3]   [4 5 3]) 

(prn @myAtom)  ; ==>  {3 3}

然而,在将上面的“相同”的东西放入一个 let 绑定之后,它就不再起作用了

(let  [ myAtom  (atom {})]    
  (map #(if (== %1 %2) (swap! myAtom assoc %1 %2 ))
              [1 2 3]   [4 5 3]) 
  (prn @myAtom)) ;;==> {} empty???

那么问题来了,在 let 绑定中 myAtom 会发生什么?怎么不见了?

4

2 回答 2

7

map是从序列中生成惰性序列,而您需要为序列中的每个项目执行某些操作(即交换),因此您需要使用doseq

编辑:(更新为@mobyte 建议)

(let  [myAtom  (atom {})
       a [1 2 3]
       b [4 5 3]]    
  (doseq [[x y] (map vector a b)]
    (if (== x y) (swap! myAtom assoc x y )))
  (prn @myAtom))

您的第一个示例有效,因为您在 REPL 中执行了每个表达式,这使得 map 操作执行其惰性操作。

我见过很多人尝试使用 map 来执行某些类似这样的操作, map 应该只用于一个目的,即将一个序列映射到另一个序列,而没有任何副作用操作。

于 2013-03-11T06:34:43.790 回答
4

正如 Ankur 提到的,最好doseq用于命令式操作:

(let [myAtom  (atom {})]    
  (doseq [[a b] (map vector 
                     [1 2 3]
                     [4 5 3])]
    (when (== a b) (swap! myAtom assoc a b )))
  (prn @myAtom))

但是您可以通过dorun在原始版本中使用来强制评估地图结果:

(let  [ myAtom  (atom {})]    
  (dorun (map #(when (== %1 %2) (swap! myAtom assoc %1 %2 ))
              [1 2 3] [4 5 3])) 
  (prn @myAtom))

PS Ankur 的版本不等同于原版

doseq

(doseq [a [1 2 3]
        b [4 5 3]]
  (println [a b]))

=> [1 4]
   [1 5]
   [1 3]
   [2 4]
   [2 5]
   [2 3]
   [3 4]
   [3 5]
   [3 3]

map

(dorun (map #(println [%1 %2]) 
            [1 2 3]
            [4 5 3]))

=> [1 4]
   [2 5]
   [3 3]
于 2013-03-11T08:30:07.737 回答