Why should I use 'apply' in Clojure?

This is what Rich Hickey said in a blog post, but I don’t understand how motivation to use is applied. Please, help.

The big difference between Clojure and CL is that Clojure is Lisp -1, so funcall is not required, and apply is only applied to apply the function to the set of arguments defined at runtime. So (apply f [i]) you can write (fi).

Also, what does he mean by β€œClojure is Lisp -1,” but funcall is not needed? I have never been programmed in CL.

thank

+41
lisp clojure
Aug 10 '09 at 20:28
source share
6 answers

You should use apply if the number of arguments passed to the function is not known at compile time (sorry, I don’t know the Clojure syntax; all that is good, resorting to the Scheme):

(define (call-other-1 func arg) (func arg)) (define (call-other-2 func arg1 arg2) (func arg1 arg2)) 

As long as the number of arguments is known at compile time, you can pass them directly, as in the above example. But if the number of arguments is not known at compile time, you cannot do this (well, you could try something like):

 (define (call-other-n func . args) (case (length args) ((0) (other)) ((1) (other (car args))) ((2) (other (car args) (cadr args))) ...)) 

but it will soon become a nightmare. Where applicable is included in the image:

 (define (call-other-n func . args) (apply other args)) 

It takes any number of arguments contained in the list specified as the last argument, and calls the function passed as the first argument to use with these values.

+51
Aug 10 '09 at 20:35
source share

The terms Lisp -1 and Lisp -2 refer to whether the functions are in the same namespace as the variables.

In Lisp -2 (i.e. 2 namespaces), the first element in the form will be evaluated as the name of the function - even if it is actually a variable name with the value of the function. Therefore, if you want to call a variable function, you need to pass the variable to another function.

In Lisp -1, such as Scheme and Clojure, variables that evaluate functions can go back to their original position, so you don't need to use apply to evaluate it as a function.

+38
Aug 10 '09 at 21:04
source share

apply basically expands the sequence and applies the function to them as separate arguments.

Here is an example:

 (apply + [1 2 3 4 5]) 

This returns 15. It basically expands to (+ 1 2 3 4 5) instead of (+ [1 2 3 4 5]) .

+28
Nov 30 '09 at 14:07
source share

You use apply to transform a function that works with multiple arguments, which works with a single sequence of arguments. You can also insert arguments before a sequence. For example, map can work with several sequences. This example (from ClojureDocs ) uses map to transfer the matrix.

 user=> (apply map vector [[:a :b] [:c :d]]) ([:a :c] [:b :d]) 

One argument inserted here is vector . Thus, apply expands to

 user=> (map vector [:a :b] [:c :d]) 

Cute!

PS To return a vector of vectors instead of a sequence of vectors, wrap it all in vec :

 user=> (vec (apply map vector [[:a :b] [:c :d]])) 

While we are here, vec can be defined as (partial apply vector) , although it is not.

Regarding Lisp -1 and Lisp -2: 1 and 2, indicate the number of things that a name can denote in a given context. In Lisp -2, you can have two different things (function and variable) with the same name. So, wherever possible, you should decorate your program with something to indicate what you mean. Fortunately, Clojure (or Scheme ...) allows you to designate only one thing, so such decorations are not needed.

+6
Dec 17 '13 at 15:50
source share

The usual pattern for application-type operations is to combine the function provided at runtime with a set of arguments, the same thing.

I have not done enough with clojure to be sure of the intricacies of this particular language, to say whether the need to use the application in this case is strictly necessary.

+2
Aug 10 '09 at 20:34
source share

Useful with protocols, especially when combined with streaming macros. I just discovered this. Insofar as cannot use a macro and extend the interface arguments at compile time , you can use an unpredictable size vector instead.

Therefore, I use this, for example, as part of the interface between an entry containing some metadata about a particular xml file and the file itself.

 (query-tree [this forms] (apply xml-> (text-id-to-tree this) forms))) 

text-id-to-tree is another method of this particular entry that parses the file into an xml zipper. In another file, I extend the protocol with a specific query that implements query-tree , specifying a chain of commands for streaming through the xml-> macro:

 (tags-with-attrs [this] (query-tree this [zf/descendants zip/node (fn [node] [(map #(% node) [:tag :attrs])])]) 

(note: this query alone will return many "null" results for tags that have no attributes. Filter and reduce for a clean list of unique values).

zf, by the way, refers to clojure.contrib.zip-filter and zip to clojure.zip. The xml-> macro is from the clojure.contrib.zip-filter.xml library, which I :use

0
Feb 24 2018-12-12T00:
source share



All Articles