Definition of function `apply`

匿名 (未验证) 提交于 2019-12-03 01:45:01

问题:

To me apply is one of the more difficult functions in Clojure. I'm looking for a good definition of its mechanics - what exactly it does, especially regarding how it 'grabs' arguments that it feeds to the function it is supplied as the first parameter. For instance this definition:

apply explodes a seqable data structure so it can be passed to a function that expects a rest parameter. For example, max takes any number of arguments and returns the greatest of all the arguments.

, from the 'Brave and True' book. Is there more to it than that? It seems to me that apply can be given many 'free' arguments (arguments not in any data structure) and will supply them to the given function. What are the rules for how apply grabs params for its function? What makes it stop?

回答1:

apply only explodes the last argument. So, for instance, this:

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

is equivalent to this:

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

All I've done in this step is take every argument except the last one and put them into a sequence (here I've used a vector, but the exact type doesn't matter), then concatenated that sequence with the last argument (which was already a sequence).

Simplying further, we get this:

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

which is equivalent to this:

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

To better understand the behavior of apply, you can think of it as working like this:

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

Just for completeness, this implementation differs from the actual implementation in two ways:

  1. Empty argument list

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

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


回答2:

just take a look at its source:

(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)))))))))

where spread is the following:

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

so, it takes as many arguments as you provide, making a list of them, expecting the last one to be a sequence, which will be concated to the previous items. And it doesnt matter whether the one of your args, except last one, is a collection, it won't be flattened.

(apply f a b c d e f g [h i j k]) fill call a function with params a b c d e f g h i j k

that's all it does.

Obviously it's up to you to make it correspond the applied fn args, if your function takes two arguments, (apply f [1 2 3 4 5]) will cause an exception.



转载请标明出处:Definition of function `apply`
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!