函数的定义`apply`

时间:2016-02-03 13:44:04

标签: clojure

对我来说apply是Clojure中比较困难的功能之一。我正在寻找它的机制的一个很好的定义 - 它到底是做什么的,特别是关于它如何'抓取'它作为第一个参数提供给它的函数的参数。例如这个定义:

  

apply会爆炸一个seqable数据结构,因此可以将它传递给一个需要rest参数的函数。例如,max接受任意数量的参数并返回所有参数中最大的参数。

,来自'勇敢而真实'book。还有更多吗?在我看来,apply可以被赋予许多“自由”参数(参数不在任何数据结构中),并将它们提供给给定的函数。 apply如何抓住params的功能有哪些规则?是什么让它停止?

2 个答案:

答案 0 :(得分:3)

apply只会爆炸最后一个参数。所以,例如,这个:

(apply f 1 2 [3 4] 5 [6 7 8])

相当于:

(apply f (concat [1 2 [3 4] 5] [6 7 8]))

我在这一步中完成的所有操作都是采用除最后一个之外的所有参数并将它们放入一个序列中(这里我使用了一个向量,但确切的类型并不重要),然后将该序列与最后一个参数(已经是序列)连接起来。

进一步简化,我们得到了这个:

(apply f [1 2 [3 4] 5 6 7 8])

相当于:

(f 1 2 [3 4] 5 6 7 8)

为了更好地理解apply的行为,您可以将其视为这样:

(defn apply [f & args]
  (.applyTo f (seq (lazy-cat (drop-last args) (last args)))))

为了完整起见,这种实现方式与实际实现方式有两点不同:

  1. 空参数列表

    ;; actual implementation
    (apply +)
    ;=> ArityException Wrong number of args (1) passed to: core/apply
    
    ;; my implementation
    (apply +)
    ;=> 0
    
  2. 懒惰

    ;; actual implementation
    (apply apply == (range))
    ;=> StackOverflowError
    
    ;; my implementation
    (apply apply == (range))
    ;=> false
    

答案 1 :(得分:2)

看看它的来源:

(defn apply
  ([^clojure.lang.IFn f args]
     (. f (applyTo (seq args))))
  ([^clojure.lang.IFn f x args]
     (. f (applyTo (list* x args))))
  ([^clojure.lang.IFn f x y args]
     (. f (applyTo (list* x y args))))
  ([^clojure.lang.IFn f x y z args]
     (. f (applyTo (list* x y z args))))
  ([^clojure.lang.IFn f a b c d & args]
     (. f (applyTo (cons a (cons b (cons c (cons d (spread args)))))))))

其中spread如下:

(defn spread
  [arglist]
  (cond
   (nil? arglist) nil
   (nil? (next arglist)) (seq (first arglist))
   :else (cons (first arglist) (spread (next arglist)))))

所以,它需要你提供的参数一样多,制作一个列表,期望最后一个是一个序列,它将与前面的项目合并。除了最后一个之外,你的一个args是否是一个集合并不重要。它不会被夷为平地。

(apply f a b c d e f g [h i j k])填写调用a b c d e f g h i j k

的函数

就是这样。

显然,你应该使它与应用的fn args相对应,如果你的函数有两个参数,(apply f [1 2 3 4 5])将导致异常。