4

假设我们有一个 multimethod foo。它有几个实现。假设当参数 of 是包含字符的字符串时调用其中一个,而当参数 offoo是包含字符的字符串时\r执行另一个。伪代码:foo\!

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

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

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

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

(foo "right!") ;; desired output:
one
two
;; => nil

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

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

4

2 回答 2

4

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

也就是说:对于单个多方法调用,多方法中的调度仅调用一次,因此除非您定义一个实现,否则无法获得您期望的结果(“正确!”打印为“一”和“二”作为参数)它实际上处理在输入字符串中同时包含\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

编辑

如果您需要访问多方法体内的整个字符串,则将其与字符一起显式传递:

; 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)))
于 2014-08-28T12:50:36.843 回答
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")
于 2014-09-01T04:31:57.797 回答