Clojure中的匿名函数

时间:2015-11-11 23:11:26

标签: clojure anonymous-function

也许这听起来很荒谬,但对我来说仍然不能完全清楚匿名函数的#应该来的区别。例如,在此示例中,我过滤了正数的除数:

(filter #(zero? (mod 6 %)) (range 1 (inc 6)))  ;;=> (1 2 3 6)

但将#放在(mod 6 %)之前会导致错误。是否有规则在这样的上下文中我的匿名函数开始,为什么#应该在(zero? ...之前?

3 个答案:

答案 0 :(得分:7)

这表明#(...)语法只是(fn [x] ...)的缩写:

(defn divides-6 [arg]
  (zero? (mod 6 arg)))

(println  (filter divides-6                   (range 1 10))) ; normal function
(println  (filter (fn [x] (zero? (mod 6 x)))  (range 1 10))) ; anonymous function
(println  (filter        #(zero? (mod 6 %))   (range 1 10))) ; shorthand version

;=> (1 2 3 6)
;=> (1 2 3 6)
;=> (1 2 3 6)

使用defn只是(def divides-6 (fn [x] ...))的简写(即deffn部分合并为defn以节省一点打字费用。如果我们只使用一次函数,我们就不需要定义全局名称divides-6。我们可以直接定义函数内联的位置。 #(...)语法只是一个简写版本,如示例所示。

请注意,表单#(...)的全名是"匿名函数文字"。您可能还会看到它被称为"功能阅读器宏"或只是"功能宏"。语法(fn [x] ...)被称为"函数特殊形式"。

答案 1 :(得分:3)

Clojure的filter函数需要一个或两个参数;无论哪种方式,第一个参数必须是函数。所以没有"规则"定义匿名函数的地方,只要最终,filter的第一个参数就是函数。

但是,在这种情况下,zero?不会返回函数,因此(zero? #(mod 6 %))会导致filter抛出错误。事实上,(zero? #(mod 6 %)也没有意义,因为zero?不会将函数作为参数。

答案 2 :(得分:2)

过滤器有两个参数:

  • 谓词(过滤器,这是一个函数)和
  • 一个集合

所以,以一种简单的方式:

(defn my-predicate [x]
 (zero? (mod 6 x)))

(def my-collection
 (range 1 (inc 6)))

(filter 
 my-filter 
 my-collection)

是一个clojure宏,或者为您预处理和重新组织代码的东西。我们可以使用macroexpand-1看到宏的结果:

 (macroexpand-1 '#(zero? (mod 6 %)))
 ; (fn* [p1__4777#] (zero? (mod 6 p1__4777#)))

或更易读的代码:

 (fn* [x] 
   (zero? 
     (mod 6 x))

对于集合的单个值,例如3,我们可以应用上述函数:

 ( (fn* [x] 
     (zero? 
       (mod 6 x))) 
   3)
 ; true

然后回到我们代码的版本,函数的输入参数隐式为%,所以:

 ( 
   #(zero? (mod 6 %))
   3) 
 ; true

最后,回到原来的函数,你明白为什么需要是定义过滤函数谓词的函数:

 (filter 
   #(zero? (mod 6 %)) 
   (range 1 (inc 6))) 
 ; (1 2 3 6)