Es mejor evitar # 'en la mayoría de los casos porque es "en su mayoría" innecesario y hace que su código sea más detallado. Existen algunas excepciones cuando se necesita alguna forma de cotización (ver el Ejemplo 4 a continuación).
Nota: Todos los ejemplos en esta publicación han sido probados en Emacs Lisp (GNU Emacs 25.2.1), pero deberían funcionar de manera idéntica en cualquier ceceo común de ANSI. Los conceptos básicos son los mismos en ambos dialectos.
explicación simple
En primer lugar, vamos a estudiar un caso en el que lo mejor es evitar citar. Las funciones son objetos de primera clase (por ejemplo, tratados como cualquier otro objeto, incluida la capacidad de pasarlos a funciones y asignarlos a variables) que se evalúan a sí mismos. Las funciones anónimas (por ejemplo, las formas lambda) son uno de esos ejemplos. Intente lo siguiente en Emacs Lisp (M-x ielm RET) o ANY ANSI common lisp.
((lambda (x) (+ x 10)) 20) -> 30
Ahora, probar la versión citado
(#'(lambda (x) (+ x 10)) 20) -> "function error" or "invalid function..."
Si utiliza insistir en el uso #', usted tiene que escribir
(funcall #'(lambda (x) (+ x 10)) 20) -> 30
Explicación detallada
Para entender realmente cuando se requiere citar, uno debe saber cómo Lisp evalúa expresiones. Sigue leyendo. Prometo hacer esto sucinto.
lo que necesita saber algunos hechos básicos acerca de Lisp:
- Lisp "siempre" evalúa cada expresión. Bueno, a menos que se cite la expresión, en cuyo caso se devuelve sin evaluar.
- Los átomos evalúan a sí mismos. Las expresiones atómicas NO son listas. Los ejemplos incluyen números, cadenas, tablas hash y vectores.
- Los símbolos (nombres de variables) almacenan dos tipos de valores. Pueden tener valores regulares y definiciones funcionales. Por lo tanto, los símbolos Lisp tienen dos espacios llamados celdas para almacenar estos dos tipos. El contenido no funcional generalmente se mantiene en la celda del valor del símbolo y funciona en la celda de función. La capacidad de mantener definiciones funcionales y no funcionales coloca simultáneamente Emacs Lisp y Common Lisp en la categoría 2-Lisp. Cuál de las dos celdas se usa en una expresión depende de cómo se usa el símbolo, más específicamente su posición en una lista. Por el contrario, los símbolos en algunos dialectos de Lisp, siendo Scheme el más conocido, pueden contener solo un valor. Scheme no tiene concepto de valor y células funcionales. Dichos Lisp se denominan colectivamente 1-Lisps.
Ahora, necesita una comprensión aproximada de cómo Lisp evalúa las expresiones S (expresiones entre paréntesis). Cada S-expresión es evaluada aproximadamente como sigue:
- Si citado, devolverlo sin evaluar
Si no indicada, obtener su CAR (por ejemplo primer elemento) y evaluarlo usando las siguientes reglas:
una . si es un átomo, simplemente devuelve su valor (por ejemplo, 3 -> 3, "pablo" -> "pablo")
b. si es una expresión S, evalúe usando el mismo procedimiento general
c. si es un símbolo, devuelva el contenido de su celda de función
Evalúe cada uno de los elementos en la CDR de la expresión S (por ejemplo, todos menos el primer elemento de la lista).
- Aplica la función obtenida desde el CAR a los valores obtenidos de cada uno de los elementos en el CDR.
El procedimiento anterior implica que cualquier símbolo en el CAR de un Unquoted S-expresión debe tener una definición funcional válida en su celda de función.
Ahora, regresemos al ejemplo del comienzo de la publicación. ¿Por qué
(#'(lambda (x) (+ x 10)) 20)
generan un error? Lo hace porque # '(lambda (x) (+ x 10)), el CAR de la expresión S, no es evaluado por el intérprete Lisp debido a la cita funcional #'.
#'(lambda (x) (+ x 10))
no es una función, pero
(lambda (x) (+ x 10))
es. Tenga en cuenta que el propósito de las citas es evitar la evaluación.Por otro lado, un formulario lambda se evalúa a sí mismo, una forma funcional, que es válida como el CAR de una lista UNQUOTED. Cuando Lisp evalúa el CAR de
((lambda (x) (+ x 10)) 20)
se pone (lambda (x) (+ x 20)), que es una función que se puede aplicar al resto de los argumentos en una lista (siempre que la longitud de la CDR es igual a la cantidad de argumentos permitidos por la expresión lambda). Por lo tanto,
((lambda (x) (+ x 10)) 20) -> 30
La pregunta es, por tanto, cuándo citar funciones o símbolos con definiciones funcionales. La respuesta es NUNCA a menos que hagas las cosas "incorrectamente". Cuando digo "incorrectamente" me refiero a que coloca una definición funcional en la celda de valor de un símbolo o celda funcional cuando debería hacer lo contrario. Ver los siguientes ejemplos para obtener una mejor comprensión:
Ejemplo 1 - funciones almacenadas en celdas de valores
Supongamos que usted necesita utilizar "aplicar" con una función que espera un número variable de argumentos. Un ejemplo de esto es el símbolo +. Lisp trata + como un símbolo regular. La definición funcional se almacena en la celda funcional de +. Se puede asignar un valor a su celda de valor si te gusta usar
(setq + "I am the plus function").
Si se evalúa
+ -> "I am the plus function"
Sin embargo, (1 + 2) sigue funcionando como se esperaba.
(+ 1 2) -> 3
La función se aplica es bastante útil en la recursión. Supongamos que quiere sumar todos los elementos en una lista. NO PUEDE escribir
(+ '(1 2 3)) -> Wrong type...
La razón es que + espera que sus argumentos sean funciones. aplicar resuelve este problema
(apply #'+ '(1 2 3)) -> (+ 1 2 3) -> 6
¿Por qué cité + arriba? Recuerde las reglas de evaluación que describí arriba. Lisp evalúa la aplicación del símbolo recuperando el valor almacenado en su celda de función. obtiene un procedimiento funcional que puede aplicarse a una lista de argumentos. Sin embargo, si no cito +, Lisp recuperará el valor almacenado en su celda de valor porque NO es el primer elemento en la expresión S. Debido a que establecemos la celda de valor de + en "Yo soy la función más", Lisp no obtiene la definición funcional mantenida en la celda de función de +. En realidad, si no hubiéramos configurado su celda de valor en "I am the plus function", Lisp habría recuperado nil, lo cual NO es una función, como lo requiere apply.
Hay una manera de usar + sin comillas con aplicar. Sí hay. Sólo se puede evaluar el siguiente código:
(setq + (symbol-function '+))
(apply + '(1 2 3))
Este evaluará a 6, como se esperaba, ya que evalúa Lisp (bajo + '(1 2 3)) ahora se encuentra la definición funcional de + almacenada en +' s celda de valor
Ejemplo 2 - Almacenamiento de definiciones funcionales en celdas de valores
supongamos que almacena una definición funcional en células valor del símbolo.Esto se lograr de la siguiente manera:
(setq AFunc (lambda (x) (* 10 x)))
Evaluación de
(AFunc 2)
genera un error porque Lisp no puede encontrar una función en la celda de la función AFunc. Puede solucionar esto utilizando funcall, que le dice a Lisp que use el valor en la celda de valores del símbolo como una definición funcional. Usted hace esto usando "funcall".
(funcall AFunc 2)
Suponiendo que la definición funcional almacenado en la celda el valor del símbolo es válida,
(funcall AFunc 2) -> 20
Se podría evitar que el uso de funcall colocando la forma de lambda en la celda de función del símbolo utilizando FSET:
(setf AFunc (lambda (x) (* 10 x)))
(AFunc 2)
Este bloque de código devolverá 20 porque lisp encuentra una definición funcional en la celda de función de AFunc.
Ejemplo 3 - Funciones locales
Digamos que usted está escribiendo una función y necesita una función que no se utilizará ningún otro lugar. Una solución típica es definir una función válida solo dentro del alcance de la principal. Prueba esto:
(defun SquareNumberList (AListOfIntegers)
"A silly function with an uncessary
local function."
(let ((Square (lambda (ANumber) (* ANumber ANumber))))
(mapcar Square AListOfIntegers)
)
)
(SquareNumberList '(1 2 3))
Este bloque de código volverá
(1 4 9)
La razón por la plaza no es citado en el ejemplo anterior es que el S-expresión se evalúa de acuerdo con las reglas que he descrito anteriormente. Primero, Lisp extrae la definición funcional de 'mapcar'. A continuación, Lisp extrae el contenido de la celda de valor de su segundo argumento (por ejemplo, 'Cuadrado'). Finalmente, devuelve '(1 2 3) no evaluado para el tercer argumento.
Ejemplo 4 - Contenido del valor de un símbolo y función de las células
Aquí es un caso cuando se requieren comillas.
(setq ASymbol "Symbol's Value")
(fset 'ASymbol (lambda() "Symbol's Function"))
(progn
(print (format "Symbol's value -> %s" (symbol-value 'ASymbol)))
(print (format "Symbol's function -> %s" (symbol-function 'ASymbol)))
)
El código anterior se evaluará como
"Symbol's value -> Symbol's Value"
"Symbol's function -> (lambda nil Symbol's Function)"
nil
Cita se requieren tanto en
(fset 'ASymbol (lambda() "Symbol's Function"))
y
(symbol-value 'ASymbol)
y
(symbol-function 'ASymbol)
porque, de lo contrario, Lisp obtendría el valor de ASymbol en cada caso, lo que evitaría que fset, symbol-value y symbol-function funcionaran correctamente.
Espero que esta larga publicación sea útil para alguien.
Soy consciente de la distinción del espacio de nombres. Pero esperaba que dado que lambda devuelve un objeto de función directamente (¿o no?), No se necesitaría una llamada a 'function' o # '. ¿Por que es esto entonces? –
Actualizó la respuesta. – danlei
Hmmm, entonces no, la lambda pura no devuelve un objeto de función ... gracias por la explicación. –