结合Clojure中的方法

时间:2014-08-28 10:55:00

标签: clojure multimethod

假设我们有一个多方法foo。它有几个实现。让我们说当foo的参数是包含字符\r的字符串时调用其中一个,而当foo的参数是包含字符{{的字符串时执行另一个1}}。伪代码:

\!

所以当我们这样调用我们的函数时:

(defmulti foo ???) ; can't come up with function..

(defmethod foo \r [_]
  (println "one"))

(defmethod foo \! [_]
  (println "two"))

重要的是,支持的方法列表不应该是严格的,而是可扩展的,因此可以在以后添加新方法而无需触及原始代码。

虽然我在过去几天里显着提高了我的Clojure技能,但我仍缺乏经验。我最好的想法是保持地图上有成对的字符 - 功能'然后手动遍历它并执行正确的功能。在这种情况下,我还需要一些界面来注册新功能等。什么是惯用解决方案?

2 个答案:

答案 0 :(得分:4)

我认为多种方法不会按照您期望的方式工作。

即:对于单个多方法调用,多方法中的调度只被调用一次,因此无法获得您期望的结果(包括'一个'和'两个& #39;打印为"对!"作为参数)除非您定义一个实际处理输入字符串中同时包含\r\!的情况的实现并打印输出你要。

这不容易扩展。

实现所需目标的更好方法是通过迭代输入字符串显式地进行多次调用:

; You want the dispatch function to just return the character passed to it.
(defmulti foo identity) 

; The argument list here is mandatory, but we don't use them at all, hence '_'
(defmethod foo \r [_] 
  (println "one"))

(defmethod foo \! [_]
  (println "two"))


; You need the default case for all the other characters
(defmethod foo :default [_]
  ())

; Iterates the string and executes foo for each character
(defn bar [s] 
    (doseq [x s] 
        (foo x)))

所以打电话

(bar "right!") 

将打印:

one
two

修改

如果你需要访问multimethod体内的整个字符串,那么将它与字符一起显式传递:

; You want the dispatch function to just return the character passed to it as the first arg.
(defmulti foo (fn [c _] c)) 


(defmethod foo \r [c s] 
  (println "one"))

(defmethod foo \! [c s]
  (println "two"))

; The default now takes two arguments which we ignore
(defmethod foo :default [_ _] ())

; Iterates the string and executes foo for each character
(defn bar [s] 
    (doseq [x s] 
        (foo x s)))

答案 1 :(得分:0)

一个简单的函数列表将允许任意条件。如果你正在处理字符串,正则表达式可能会让你的生活更简单:

;; start with some functions
(defn on-r [x]
  (when (re-find #"r" x)
    "one"))
(defn on-! [x]
  (when (re-find #"!" x)
    "two"))
(def fns (atom [on-r on-!]))

;; call all functions on some value
(keep #(% "right!") @fns)
=> ("one" "two")
(keep #(% "aaaa") @fns)
=> ()

;; later add more functions
(defn on-three [x]
  (when (= 3 (count x))
    "three"))
(swap! fns conj on-three)
(keep #(% "bar") @fns)
=> ("one" "three")

;; or just use different rules altogether
(def other-fns [#(when (rand-nth [true false])
                   (str % (rand-int 10)))
                #(when (nil? %) "nil")])
(keep #(% nil) other-fns)
=> ("3" "nil")