2012-02-18 49 views
8

Clojure中,有可能解构地图的一些关键是这样的:在Clojure中,如何解构地图的所有键?

(let [{:keys [cpp js]} {:cpp 88 :js 90}] 
    (println js); 90 
    (println cpp); 88 
) 

有没有办法来解构所有的地图的钥匙?

也许是这样的:

(let [{:all-the-keys} {:cpp 88 :js 90}] 
    (println js); 90 
    (println cpp); 88 
) 

回答

22

不是真的,这不会是一个好主意。想象一下:

(let [{:all-the-keys} m] 
    (foo bar)) 

foo和bar globals?当地人?你应该从m中提取的键?如果m 有时包含foo键,而foo也是全局函数,该代码应该执行什么操作?有时候你会打电话给全球,有时你会调用存储在m中的函数?

忽略技术问题(其中大部分可能可以克服),这对于可读性和可预测性来说确实是一场灾难。只要明确你想要拔出什么键;如果您经常想要提取相同的十个键,您可以编写一个简单的宏,如(with-person p body),这可以简化您常见的情况。

6

你可以写一个宏来做到这一点(有效地创建一个小型DSL),但我不认为这是由于以下原因,一个非常好的主意:

  • 为了建立正确的编译时文字js和cpp,你需要在编译时解构map。这对于你可以用它做什么而言是相当有限的(例如,你必须提前指定键,例如它不能用于更高阶的函数)
  • 宏一般是个坏主意,当更简单的方法将做的工作(见下文)

我在地图上只是用一个简单的doseq你的情况建议到循环:

(let [my-map {:cpp 88 :js 90}] 
    (doseq [[k v] my-map] 
    (println v))) 

需要注意的是:

  • 你可以用解构如上提取两个关键k并从每个映射条目
  • 我以前doseq而不是for,因为它是不懒惰,似乎在这个例子中,你只使用循环的价值vprintln副作用。
  • 如果您想要一个惰性的值序列(88 90),那么for将是适当的。
7

这个问题很老,所以你可能已经忘记了它,但当我试图做同样的事情时它出现在谷歌,所以如果我发布我的解决方案,它可能会帮助别人。

(defmacro let-map [vars & forms] 
    `(eval (list 'let (->> ~vars keys 
         (map (fn [sym#] [(-> sym# name symbol) (~vars sym#)])) 
         (apply concat) vec) 
       '~(conj forms 'do)))) 

这基本上转化地图{:cpp 88 :js 90}到装订形式[cpp 88 js 90]然后构建一个let结合,以执行一些EVAL柔术,以确保这种情况发生在运行时一起。

(def test-map {:cpp 88 :js 90}) 
(let-map test-map 
    (println js) 
    (println cpp)) 
;=> 90 
;=> 88 
+1

哦,cmon,这是实际回答标题问题的唯一答案。谢谢。 – desudesudesu 2013-01-17 00:18:43

+1

这是一个非常聪明的宏。让我们花一点时间来认识它的独创性,然后承诺我们永远不要在生产代码中写任何类似的东西。 – MichaelBlume 2013-02-14 01:32:32

+0

它看起来可能不是最佳实践,但我甚至会争辩说这个构造有有效的用例。 – 2015-03-01 12:23:06

相关问题