Function 在Clojure中传递方法名称以进行评估的惯用方法?

Function 在Clojure中传递方法名称以进行评估的惯用方法?,function,hash,clojure,lisp,idioms,Function,Hash,Clojure,Lisp,Idioms,我正在传递一个函数的名称,以便在另一个方法中使用 (defn mapper [m function] (cond (= '() m) '() true (cons (function (first m)) (mapper (rest m) function)))) (println (mapper '((blue red)(green red)(white red)) #'first)) 在clojure中有没有更惯用的方法来实现这一点?我相信你的方法

我正在传递一个函数的名称,以便在另一个方法中使用

(defn mapper [m function]
  (cond
   (= '() m) '()
   true (cons (function (first m))
            (mapper (rest m) function))))

(println (mapper '((blue red)(green red)(white red)) #'first))

在clojure中有没有更惯用的方法来实现这一点?

我相信你的方法基本上是惯用的。Clojure自己的
map
使用:

(defn mapper [coll f]
 (when-let [s (seq coll)]
    (cons (f (first s)) (mapper (rest s) f))))
我已经严重缩短了它-原始生成一个延迟序列,处理多个集合,分块序列,等等。顺便说一下-我假设您想要传递的是实际的函数,而不是它的名称


coll
f
分别是表示集合和函数的惯用arg名称

我觉得你的版本不错。您将在clojure代码库中看到的常用名称是集合的“coll”。我还看过哈斯克尔风格的“xs”。您也可以参考有关各种约定的说明

回到例子:两个观察结果

  • Use:else将'cond'作为转义条件,而不是常见的Lisp样式'T'
  • 不要假设列表,而是考虑序列
  • 考虑到这两点,如果我重写您的代码:

    user> (defn mapper [coll f]
            (cond
              (not (seq coll)) nil
              :else (conj (mapper (next coll) f)
                          (f (first coll)))))
    #'user/mapper
    user> (mapper '(1 2 3) #(* % %))
    (1 4 9)
    user> (mapper [1 2 3] #(* % %))
    (1 4 9)
    
    注意,就集合而言,conj做的是“正确的事情”。它将新元素添加到列表的头部、向量的尾部等等。还要注意在传统的lisp中使用了“next”而不是first/restnext'返回第一个元素之后的元素序列。因此,可以通过对集合进行排序来检查空值,对于空列表或空向量,该集合将返回nil。这样,它适用于所有系列。

    • 更喜欢向量而不是列表。在大多数情况下,您不必引用向量,而且它在很多方面都有更好的性能,比如随机访问。列表在Clojure中的使用要比在其他Lisp中少得多
    • 喜欢关键字而不是引用符号。关键字突出显示为“常量字符串”或枚举值。Clojure中的关键字可以属于名称空间,因此它们具有符号的所有优点。再说一次,没有必要引用关键字,这很好。引用符号在Clojure中很少使用,除非您正在编写宏
    • #“first
      是名为“first”的变量
      first
      是称为“first”的风险值,即fn。在这种情况下,
      (#'first foo)
      (first foo)
      给出相同的答案,但是
      #'first
      每次调用时都会进行额外的解引用。所以不要这样做,除非你想让这种去引用一次又一次地发生。通常不需要使用
      #'
    • 内置的
      map
      是惰性的,而您的则不是。内置的
      map
      利用分块seq获得更好的性能,而您的没有。惯用代码不必是惰性的或使用分块的seq,但请记住,内置代码具有一些这种魔力。所以好好利用一下
    • 空seq的惯用测试不是
      (='()x)
      ,而是
      (seq x)
      ,如果
      x
      为空,则返回
      nil
      。请注意,在Clojure中,
      (='()nil)
      为false
    • 如果您确实需要使用空列表(您很少需要这样做),您不必引用它。只需使用
      ()
    • 内置的
      map
      首先接受函数参数,因为它接受多个集合参数。当函数接受多个参数时,这些参数在参数列表中必须排在最后。我认为另一种方式读起来也更好:“(mapfcoll):在这个集合中映射这个函数”
    • 如果只有两个选项,则无需使用
      cond
      。如果,则可以使用
      。如果
      if
      中的一个分支返回
      nil
      ,则可以在
      时使用
      。在适当的时候使用
      如果
      是很好的,因为它们会立即向读者传达你的意图,而
      cond
      可以做任何事情,迫使读者阅读更多
    RafałDowgird的版本是惯用的,只是我会颠倒论点的顺序。我这样称呼它:

    user> (mapper first [[:blue :red] [:green :red] [:white :red]])
    (:blue :green :white)
    

    我想最惯用的方法是使用函数:
    (map first'((蓝红色)(绿红色)(白红色))
    。同意翻转参数。或者
    (defn mapper[coll f&args]…)
    ,其中
    args
    作为附加的args传递给f,这样就可以实现
    (mapper[5 2 3]+1)
    之类的功能。