online - emacs lisp
¿Por qué debería usar ''aplicar'' en Clojure? (6)
Aplicar es útil con los protocolos, especialmente en conjunción con las macros de subprocesamiento. Acabo de descubrir esto. Como no puede usar & macro para expandir los argumentos de la interfaz en tiempo de compilación , puede aplicar un vector de tamaño impredecible.
Así que uso esto, por ejemplo, como parte de una interfaz entre un registro que contiene algunos metadatos sobre un archivo xml particular y el archivo en sí.
(query-tree [this forms]
(apply xml-> (text-id-to-tree this) forms)))
text-id-to-tree
es otro método de este registro particular que analiza un archivo en una cremallera xml. En otro archivo, extiendo el protocolo con una consulta particular que implementa query-tree
, especificando una cadena de comandos que se enhebrarán a través de xml-> macro:
(tags-with-attrs [this]
(query-tree this [zf/descendants zip/node (fn [node] [(map #(% node) [:tag :attrs])])])
(nota: esta consulta por sí misma arrojará muchos resultados "nulos" para las etiquetas que no tienen atributos. Filtra y reduce para obtener una lista limpia de valores únicos).
zf, por cierto, se refiere a clojure.contrib.zip-filter y zip a clojure.zip. La macro xml-> es de la biblioteca clojure.contrib.zip-filter.xml, que yo :use
Esto es lo que dijo Rich Hickey en una de las publicaciones del blog, pero no entiendo la motivación para usar apply. Por favor ayuda.
Una gran diferencia entre Clojure y CL es que Clojure es un Lisp-1, por lo que funcall no es necesario, y apply solo se usa para aplicar una función a una colección de argumentos definida en tiempo de ejecución. Entonces, (aplicar f [i]) se puede escribir (fi).
Además, ¿qué quiere decir con "Clojure is Lisp-1" y no se necesita funcall? Nunca he programado en CL.
Gracias
El patrón habitual para las operaciones de tipo aplicar es combinar una función proporcionada en tiempo de ejecución con un conjunto de argumentos, ídem.
No he hecho lo suficiente con Clojure para poder confiar en las sutilezas de ese idioma en particular para saber si el uso de la aplicación en ese caso sería estrictamente necesario.
Los términos Lisp-1 y Lisp-2 se refieren a si las funciones están en el mismo espacio de nombres como variables.
En un Lisp-2 (es decir, 2 espacios de nombres), el primer elemento de un formulario se evaluará como un nombre de función, incluso si en realidad es el nombre de una variable con un valor de función. Entonces, si desea llamar a una función variable, debe pasar la variable a otra función.
En un Lisp-1, como Scheme y Clojure, las variables que evalúan las funciones pueden ir en la posición inicial, por lo que no es necesario usar apply
para evaluarlo como una función.
Utiliza apply
para convertir una función que funciona en varios argumentos en una que funciona en una única secuencia de argumentos. También puede insertar argumentos antes de la secuencia. Por ejemplo, el map
puede funcionar en varias secuencias. Este ejemplo (de ClojureDocs ) usa un map
para transponer una matriz.
user=> (apply map vector [[:a :b] [:c :d]])
([:a :c] [:b :d])
El único argumento insertado aquí es vector
. Entonces la apply
expande a
user=> (map vector [:a :b] [:c :d])
¡Linda!
PD: para devolver un vector de vectores en lugar de una secuencia de vectores, ajuste todo en vec
:
user=> (vec (apply map vector [[:a :b] [:c :d]]))
Mientras estamos aquí, vec
podría definirse como (partial apply vector)
, aunque no lo es.
Con respecto a Lisp-1 y Lisp-2: el 1 y 2 indican el número de cosas que un nombre puede denotar en un contexto dado. En un Lisp-2, puede tener dos cosas diferentes (una función y una variable) con el mismo nombre. Entonces, donde sea que sea válido, necesitas decorar tu programa con algo que indique a qué te refieres. Afortunadamente, Clojure (o Scheme ...) permite que un nombre denote solo una cosa, por lo que no se necesitan tales decoraciones.
Utilizaría apply , si la cantidad de argumentos para pasar a la función no se conoce en tiempo de compilación (lo siento, no conozco bien la sintaxis de Clojure, recurriendo a Scheme):
(define (call-other-1 func arg) (func arg))
(define (call-other-2 func arg1 arg2) (func arg1 arg2))
Siempre que se conozca la cantidad de argumentos en tiempo de compilación, puede pasarlos directamente como se hace en el ejemplo anterior. Pero si la cantidad de argumentos no se conoce en tiempo de compilación, no puede hacer esto (bueno, podría intentar algo así):
(define (call-other-n func . args)
(case (length args)
((0) (other))
((1) (other (car args)))
((2) (other (car args) (cadr args)))
...))
pero eso se convierte en una pesadilla lo suficientemente pronto. Ahí es donde aplicar entra la imagen:
(define (call-other-n func . args)
(apply other args))
Toma la cantidad de argumentos que contiene la lista como último argumento y llama a la función pasada como primer argumento para aplicar con esos valores.
apply
básicamente desenvuelve una secuencia y les aplica la función como argumentos individuales.
Aquí hay un ejemplo:
(apply + [1 2 3 4 5])
Eso devuelve 15. Básicamente se expande a (+ 1 2 3 4 5)
, en lugar de (+ [1 2 3 4 5])
.