在Clojure中,如何重构一个地图的所有键?

问题描述 投票:10回答:3

在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
 )
clojure
3个回答
25
投票

不见得,也不会是个好主意。试想一下。

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

foo和bar是globals吗?是局部的吗?你应该从m中提取的键?如果m 有时 包含一个foo键,而foo也是一个全局函数? 有时你调用全局函数,有时你调用存储在m中的函数?

忽略技术上的问题(可以克服),这对可读性和可预测性来说真的是一场灾难。只要明确你要调出什么键就可以了,如果你经常要调出同样的十个键,你可以写一个简单的宏,如 (with-person p body) 这为你简化了那个常见的情况。


11
投票

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

(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 绑定,同时执行一些评价-jitsu来确保这在运行时发生。

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

7
投票

你可以写一个宏来实现这一点(有效地创建一个迷你DSL),但我认为这不是一个好主意,原因如下。

  • 为了创建正确的编译时字元js和cpp,你需要在编译时重构地图。这将会对你能用它做的事情造成很大的限制(你必须事先指定键,而且它不能在高阶函数中使用,例如
  • 当一个更简单的方法可以完成工作时,宏通常是一个糟糕的主意(见下文)。

我建议只用一个简单的 剂量q 在你的情况下,在地图上循环。

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

注意:

  • 你可以像上面一样使用反结构化来提取键 k 和价值 v 从每个地图条目
  • 我用的是 剂量q 而非 对于 因为它是非懒惰的,而且在这个例子中,你似乎只对循环的 打印 副作用。
  • 如果你想要一个懒惰的数值序列(88 90),那么你就需要 对于 适当。
© www.soinside.com 2019 - 2024. All rights reserved.