在Clojure中,如何解构地图的所有键?

vie*_*bel 9 clojure

在clojure中,可以像这样构造一些地图的键:

(let [{:keys [cpp js]} {:cpp 88 :js 90}] 
   (println js); 90
   (println cpp); 88
 )
Run Code Online (Sandbox Code Playgroud)

有没有办法解构地图的所有键?

也许是这样的:

(let [{:all-the-keys} {:cpp 88 :js 90}] 
   (println js); 90
   (println cpp); 88
 )
Run Code Online (Sandbox Code Playgroud)

ama*_*loy 24

不是真的,这不是一个好主意.想像:

(let [{:all-the-keys} m]
  (foo bar))
Run Code Online (Sandbox Code Playgroud)

是foo和bar全局?当地人?你应该从m中提取的密钥?如果m 有时包含foo键,那么这段代码应该怎么办?foo也是一个全局函数?有时你调用全局,有时你调用存储在m中的函数?

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


one*_*ute 10

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

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

这基本上将地图{:cpp 88 :js 90}转换为绑定形式[cpp 88 js 90]然后构造let绑定,同时执行一些eval-jitsu以确保在运行时发生这种情况.

(def test-map {:cpp 88 :js 90})
(let-map test-map
  (println js)
  (println cpp))
;=> 90
;=> 88
Run Code Online (Sandbox Code Playgroud)

  • 这是一个非常聪明的宏。让我们花一点时间来认识它的独创性,然后向自己保证不要在生产代码中编写任何类似的东西。 (3认同)

mik*_*era 6

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

  • 为了创建正确的编译时文字js和cpp,您需要在编译时对地图进行解构.就你可以用它做什么而言,这将是非常有限的(你必须提前指定键,并且它不能用于高阶函数,例如)
  • 当更简单的方法完成工作时,宏通常是一个坏主意(见下文)

我建议你在你的情况下使用一个简单的doseq循环遍历地图:

(let [my-map {:cpp 88 :js 90}]
  (doseq [[k v] my-map]
    (println v)))
Run Code Online (Sandbox Code Playgroud)

注意:

  • 您可以使用上述解构来从每个映射条目中提取键k和值v
  • 我用doseq,而不是,因为它是不懒惰,似乎在这个例子中,你使用的是循环只为的println副作用.
  • 相反,如果你想要的值(88 90)的懒惰序列然后用于将是适当的.