我有一个函数返回值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))
多方法是否可以到达这里?如果是这样,我怎样才能完成我想要做的事情?
答案 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这种情况的正确方法。