有没有办法在多方法中重用多个方法之间的解构?
(defmulti foo (fn [x] (:a x)))
(defmethod foo :1 [{:keys [a b c d e]}] (str a b c d e))
(defmethod foo :2 [a] "")
(defmethod foo :3 [a] "")
现在这是一个简单的例子,但是想象一下我们使用嵌套地图进行了更为复杂的解构,我希望在我的所有defmethods
上使用它来获取foo。我该怎么做?
答案 0 :(得分:6)
实际的解决方案是仅使用每种方法所需的密钥。关于解构的一个重要注意事项是,您不必绑定您正在解构的集合中的每个值。我们假设传递给此多方法的每个地图都包含:a
到:e
的键,但每种方法只需要几个键。你可以这样做:
; note: a keyword can act as a function; :a here is equivalent to (fn [x] (:a x))
(defmulti foo :a)
(defmethod foo :1 [{:keys [a b c d e]}] (str a b c d e))
(defmethod foo :2 [{:keys [b d]}] (str b d))
(defmethod foo :3 [{:keys [c e a]}] (str a c e))
如果您有一个复杂的嵌套结构,并且想要获取特定值,则可以省略您不需要的密钥,或者根据您的使用情况,在let
内绑定函数定义最终可能更容易阅读。史蒂夫·洛什的Caves of Clojure浮现在脑海中 - 在Clojure中从头开始编写一个roguelike文本冒险游戏,他使用嵌套地图来表示游戏的状态。最初,他使用解构来编写一些函数来访问游戏状态"的内部位置。地图,例如:
(defmethod draw-ui :play [ui {{:keys [tiles]} :world :as game} screen]
...
但是later,他决定通过将解构转换为let绑定来使这段代码更具可读性:
(defmethod draw-ui :play [ui game screen]
(let [world (:world game)
tiles (:tiles world)
...
关键是,如果您正在使用深层嵌套的结构并且希望保持代码简单(特别是如果您正在编写多方法,其中多个方法采用与参数相同的结构),您可能希望避免使用解构,只需使用let
绑定来抓取您想要的部分。 get-in
是一个很好的工具,可以简单地从嵌套集合中获取值。回到Clojure洞穴的例子,如果史蒂夫只需要瓷砖,他就可以做到这样的事情:
(defmethod draw-ui :play [ui game screen]
(let [tiles (get-in game [:world :tiles])
...
就我个人而言,我发现使用{{:keys [tiles]} :world :as game}
来修复函数参数要容易得多。
修改强>
如果确实想要避免为每个多方法重复解构,并且希望每个方法都具有相同的绑定,那么可以编写一个宏:
(defmulti foo :a)
(defmacro deffoomethod [dispatch-val & body]
`(defmethod foo ~dispatch-val [{:keys [~'a ~'b ~'c ~'d ~'e]}]
~@body))
(deffoomethod 1 (str a b c d e))
(deffoomethod 2 (str b d))
(deffoomethod 3 (str a c e))
(foo {:a 1 :b 2 :c 3 :d 4 :e 5})
;=> "12345"
(foo {:a 2 :b \h :d \i})
;=> "hi"
(foo {:a 3 :b \x :c 0 :d \x :e 0})
;=> "300"
但是,我不推荐这种方法,因为它打破了宏观卫生。使用此宏的任何人都必须记住它将符号a
到e
绑定到参数中的相应键,这可能会有问题。