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

时间:2014-10-22 20:39:15

标签: 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 #",")))

2 个答案:

答案 0 :(得分:5)

首先要做的事情是:(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上定义多个调度,那么方法应该由类参数化,而不是关键字占位符。我更改了多功能,因此它适用于任何Clojure知道如何作为seq处理的东西。此外,作为一点bikeshed,最好使用type,这为区分class未提供的Clojure代码中的输入提供了一些区别:

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)

答案 1 :(得分:2)

你的第一个例子是一个名为dispatching on type的技术的混淆应用程序。它是混淆的,因为在消息传递样式中,调用者必须将类型传递给您的函数。

因为在每种情况下你只使用其中一个关键字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中,它通常被多态构造protocols取代,e。 G:

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

Multimethods允许相同的功能,并通过将调度机制与操作过程分离并提供数据导向编程的可加性,为消息传递调度类型提供额外的灵活性。但它们的执行速度比协议慢。

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