2010-07-24 15 views
33

me gustaría saber por qué código Lisp más común que veo tiene cosas como¿Por qué # 'se usa antes de lambda en Common Lisp?

(mapcar #'(lambda (x) (* x x)) '(1 2 3))

en lugar de sólo

(mapcar (lambda (x) (* x x)) '(1 2 3)),

que parece funcionar tan bien. Estoy empezando a aprender Common Lisp, y teniendo algunos antecedentes en Scheme, esto me intriga.

Edición: Sé que necesita # 'con nombres de funciones porque viven en un espacio de nombre diferente al de las variables. Mi pregunta es solo # 'antes de lambda, ya que lambda ya devuelve un objeto de función (creo). El hecho de que # '- menos lambdas funciona debido a una expansión macro simplemente lo hace más intrigante ...

Respuesta

36

#'foo es un abbreviation por (function foo) por el lector.

En CL, hay varios espacios de nombres diferentes, #'foo o (function foo) devolverá el valor funcional de foo.

es posible que desee search for "Lisp-1 vs. Lisp-2", compruebe otra Stackoverflow questions, o leer un old article py Pitman and Gabriel con el fin de aprender más sobre el concepto de múltiples espacios de nombres (también llamados ranuras o células de símbolos).

La razón de que, en el caso de lambda la #' puede omitirse es decir, que se trata de una macro en CL, que se expande thusly (tomado de la Hyperspec):

(lambda lambda-list [[declaration* | documentation]] form*) 
== (function (lambda lambda-list [[declaration* | documentation]] form*)) 
== #'(lambda lambda-list [[declaration* | documentation]] form*) 

#' todavía puede usarse por razones históricas (creo que en Maclisp lambda s no se expandió a la forma de función), o porque algunas personas piensan, que "etiquetar" lambdas con sharpquotes puede hacer que el código sea más legible o coherente. Puede haber algunos casos especiales en los que esto haga la diferencia, pero en general, no importa qué forma elija.

Supongo que se puede pensar así: (function (lambda ...)) devuelve la función (lambda ...) crea. Tenga en cuenta que lambda en CL Hyperspec tiene ambos a macro AND a symbol entry. A partir de este último:

Una expresión lambda es una lista que puede puede utilizar en lugar de un nombre de función en ciertos contextos para denotar una función describiendo directamente su comportamiento en lugar de indirectamente por referencia a la nombre de una función establecida.

Desde el documentation de function:

Si el nombre es una expresión lambda, se devuelve un cierre léxica .

Creo que la diferencia también se relaciona con llamar a las formas lambda como este: ((lambda ...) ...) donde es tratada como una forma de evaluar, frente a (funcall #'(lambda ...) ...). Si desea leer más sobre el tema, hay un c.l.l thread al respecto.

Algunas citas de ese hilo:

(lambda (x) ... por sí mismo es sólo algunas estructura de lista no indicada. Es su apariencia como un argumento a la FUNCIÓN forma especial (function (lambda (x) ... que hace que el objeto función de que exista

y:

También se ve agravado por el hecho de que la macro LAMBDA era una más bien tarde además del ANSI Common Lisp, por lo que todos de los muchachos muy viejos (es decir, como yo) aprendieron su ceceo cuando necesitó suministrar el # 'a la expresión lambda en las funciones de mapeo. De lo contrario, se invocaría la función lambda inexistente .

La adición de macro ha cambiado eso, pero algunos de nosotros estamos demasiado en nuestro camino para querer cambiar.

+1

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? –

+0

Actualizó la respuesta. – danlei

+0

Hmmm, entonces no, la lambda pura no devuelve un objeto de función ... gracias por la explicación. –

5

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:

  1. Lisp "siempre" evalúa cada expresión. Bueno, a menos que se cite la expresión, en cuyo caso se devuelve sin evaluar.
  2. Los átomos evalúan a sí mismos. Las expresiones atómicas NO son listas. Los ejemplos incluyen números, cadenas, tablas hash y vectores.
  3. 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:

  1. Si citado, devolverlo sin evaluar
  2. 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

  3. 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).

  4. 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.