Clojure multimethod调度函数和值

时间:2013-10-25 15:39:46

标签: clojure

我有一个函数返回值v存在的seq中的索引:

(defn indexes-of [v s]
  (map first (filter #(= v (last %)) (zipmap (range) s))))

我想要做的是扩展它以应用任何存在测试的任意函数。我的想法是使用多方法,但我不确定如何检测一个函数。我想这样做:

(defmulti indexes-of ???)
(defmethod indexes-of ??? [v s]  ;; v is a function
  (map first (filter v (zipmap (range) s))))
(defmethod indexes-of ??? [v s]  ;; v is not a function
  (indexes-of #(= v %) s))

多方法是否可以到达这里?如果是这样,我怎样才能完成我想要做的事情?

3 个答案:

答案 0 :(得分:1)

(defmulti indexes-of (fn [v _]
                       (if (fn? v)
                         :function
                         :value)))
(defmethod indexes-of :function
  [f coll]
  (keep-indexed (fn [i v] (when (f v) i)) coll))

(defmethod indexes-of :value
  [v coll]
  (indexes-of (partial = v) coll))

答案 1 :(得分:1)

如何更简单,更通用:

(defn index-matches [predicate s]
  (map first (filter (comp predicate second) (map vector (range) s))))

user> (index-matches even? (reverse (range 10)))
(1 3 5 7 9)
user> (index-matches #{3} [0 1 2 3 1 3 44 3 1 3])
(3 5 7 9)

感谢lgrapenthin提出的建议,此功能现在对懒惰输入也有效:

user> (take 1 (index-matches #{300000} (range)))
(300000)

答案 2 :(得分:1)

如果你想使用多方法,它应该在过滤功能上,这是根据existence test类型改变的功能。

所以

(defmulti filter-test (fn [value element] 
                        (cond 
                         (fn? value) :function
                         :else :value)))

(defmethod filter-test :function
  [value element]
   (apply value [element]))

(defmethod filter-test :value
   [value element]
  (= value element))

(defn indexes-of [v s]
   (map first (filter #(filter-test v (last %)) (zipmap (range) s))))

考虑到JVM不支持开箱即用的第一类函数或lambdas,因此没有“函数”数据类型可以调度,这就是fn?测试的原因。

尽管predicate提出的noisesmith解决方案是IMO这种情况的正确方法。