Clojure/FP:将函数应用于运算符的每个参数

Joh*_*ian 4 functional-programming clojure operators higher-order-functions

假设我有几个向量

(def coll-a [{:name "foo"} ...])
(def coll-b [{:name "foo"} ...])
(def coll-c [{:name "foo"} ...])
Run Code Online (Sandbox Code Playgroud)

并且我想看看第一个元素的名称是否相等.

我可以

(= (:name (first coll-a)) (:name (first coll-b)) (:name (first coll-c)))
Run Code Online (Sandbox Code Playgroud)

但随着更多功能的组合,这很快就会变得疲惫和过于冗长.(也许我想比较第一个元素名称的最后一个字母?)

直接表达计算的本质似乎是直观的

(apply = (map (comp :name first) [coll-a coll-b coll-c]))
Run Code Online (Sandbox Code Playgroud)

但它让我想知道这种事情是否有更高级别的抽象.

我经常发现自己比较/以其他方式操作通过应用于多个元素的单个合成来计算的东西,但是地图语法看起来有点偏离我.

如果我要回家酿造某种运算符,我会想要语法

(-op- (= :name first) coll-a coll-b coll-c)
Run Code Online (Sandbox Code Playgroud)

因为大多数计算都表达在(= :name first).

我想要一个抽象应用于运算符和应用于每个参数的函数.也就是说,它应该与比较一样容易.

(def coll-a [{:name "foo" :age 43}])
(def coll-b [{:name "foo" :age 35}])
(def coll-c [{:name "foo" :age 28}])

(-op- (+ :age first) coll-a coll-b coll-c)
; => 106
(-op- (= :name first) coll-a coll-b coll-c)
; => true
Run Code Online (Sandbox Code Playgroud)

就像是

(defmacro -op- 
  [[op & to-comp] & args]
  (let [args' (map (fn [a] `((comp ~@to-comp) ~a)) args)]
    `(~op ~@args')))
Run Code Online (Sandbox Code Playgroud)
  • 在clojure中有没有惯用的方法,我可以使用一些标准的库函数?
  • 这种表达方式有名称吗?

exu*_*ero 7

对于您的附加示例,我经常使用transduce:

(transduce
  (map (comp :age first))
  +
  [coll-a coll-b coll-c])
Run Code Online (Sandbox Code Playgroud)

您的相等用例比较棘手,但您可以创建自定义缩减函数来维护类似的模式.这是一个这样的功能:

(defn all? [f]
  (let [prev (volatile! ::no-value)]
    (fn
      ([] true)
      ([result] result)
      ([result item]
       (if (or (= ::no-value @prev)
               (f @prev item))
         (do
           (vreset! prev item)
           true)
         (reduced false))))))
Run Code Online (Sandbox Code Playgroud)

然后用它作为

(transduce
  (map (comp :name first))
  (all? =)
  [coll-a coll-b coll-c])
Run Code Online (Sandbox Code Playgroud)

语义与您的-op-宏非常相似,同时更具惯用性Clojure和更具可扩展性.其他Clojure开发人员将立即了解您的使用情况transduce.他们可能不得不研究自定义缩减功能,但这些功能在Clojure中很常见,读者可以看到它如何适应现有模式.此外,如果为简单的map-and-apply不起作用的用例创建新的缩减函数,应该是相当透明的.换能功能也可与其它变换,例如可以由filtermapcat,对于情况下,当你具有更复杂的初始数据结构.