基于参数的Clojure灵活功能设计



根据提供的关键字参数的值,我有一些函数的行为不同。对于这个问题,我想知道哪些函数的行为略有不同,这取决于所提供的参数类型。

示例函数,增加列表中的每个元素:

(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)))

多方法允许相同的功能,并通过将调度机制与操作过程解耦并提供数据导向编程的可添加性,在类型上的消息传递调度上提供额外的灵活性。不过,它们的执行速度比协议慢。

在您的示例中,目的是基于类型进行调度,因此您不需要多方法,协议是合适的技术。

最新更新