根据提供的关键字参数的值,我有一些函数的行为不同。对于这个问题,我想知道哪些函数的行为略有不同,这取决于所提供的参数类型。
示例函数,增加列表中的每个元素:
(defn inc-list [& {:keys [list-str list]}]
(let [prepared-list (if-not (nil? list) list (clojure.string/split list-str #","))]
(map inc prepared-list)))
制作一个测试参数类型的多方法有意义吗?我以前并没有使用过多种方法,不确定使用它们的正确时间。如果这是一个好主意,下面的例子有意义吗?
示例:
(defn inc-coll [col] (map inc col))
(defmulti inc-list class)
(defmethod inc-list ::collection [col] (inc-col col))
(defmethod inc-list String [list-str]
(inc-col
(map #(Integer/parseInt %)
(clojure.string/split list-str #",")))
首先:(map 'inc x)
将x
中的每个项视为关联集合,并查找由键'inc
索引的值。
user> (map 'inc '[{inc 0} {inc 1} {inc 2}])
(0 1 2)
你可能想要inc
而不是
user> (map inc [0 1 2])
(1 2 3)
接下来,我们将尝试inc
作为一个字符串,string/split
的args乱序,以及一些拼写错误。
如果在class
上定义要调度的multi,那么方法应该由Class参数化,而不是关键字占位符。我更改了multi,这样它就可以处理Clojure知道如何将其视为seq的任何内容。此外,作为一种循环,最好使用type
,它为区分Clojure代码中class
没有提供的输入提供了一些区别:
user> (type (with-meta {:a 0 :b 1} {:type "foo"}))
"foo"
综合起来:
user> (defn inc-coll [col] (map inc col))
#'user/inc-coll
user> (defmulti inc-list type)
nil
user> (defmethod inc-list String [list-str]
(inc-coll (map #(Integer/parseInt %) (clojure.string/split list-str #","))))
#<MultiFn clojure.lang.MultiFn@6507d1de>
user> (inc-list "1,10,11")
(2 11 12)
user> (defmethod inc-list clojure.lang.Seqable [col] (inc-coll (seq col)))
#<MultiFn clojure.lang.MultiFn@6507d1de>
user> (inc-list [1 2 3])
(2 3 4)
您的第一个示例是一个名为类型调度的技术的模糊应用程序。它是模糊的,因为在消息传递样式中,调用者必须将类型传递给函数。
由于在任何情况下都只使用其中一个关键字args,因此可以将其定义为:
(defn inc-list
[m l]
(->> (case m ;; message dispatch
:list l
:list-str (map #(edn/read-string %) (str/split #",")) l)
(map inc)))
呼叫者可以不用通过m:
(defn inc-list
[l]
(->> (cond (string? l) (map ...)
:else l)
(map inc)))
这种技术的主要缺点是,当向代码库引入新类型时,必须修改操作过程代码。
在Clojure中,它通常被多态性构造协议所取代,例如:
(defprotocol IncableList
(inc-list [this]))
可以在任何类型上实现,例如
(extend-type clojure.lang.Seqable
IncableList
(inc-list [this] (map inc this)))
(extend-type String
IncableList
(inc-list [this] (map #(inc ...) this)))
多方法允许相同的功能,并通过将调度机制与操作过程解耦并提供数据导向编程的可添加性,在类型上的消息传递和调度上提供额外的灵活性。不过,它们的执行速度比协议慢。
在您的示例中,目的是基于类型进行调度,因此您不需要多方法,协议是合适的技术。