tutorial programar programacion net lenguaje español compiler common common-lisp

programar - ¿Por qué#''se usa antes de lambda en Common Lisp?



https common lisp net (2)

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
Primero, estudiemos un caso cuando lo mejor es evitar las citas. 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, formas lambda) son un ejemplo de esto. Pruebe lo siguiente en Emacs Lisp (Mx ielm RET) o ANY ANSI common lisp.

((lambda (x) (+ x 10)) 20) -> 30

Ahora, prueba la versión citada

(#''(lambda (x) (+ x 10)) 20) -> "function error" or "invalid function..."

Si usa insistir en usar # '', debe escribir

(funcall #''(lambda (x) (+ x 10)) 20) -> 30

EXPLICACIÓN DETALLADA
Para comprender realmente cuándo se requiere citar, uno debe saber cómo Lisp evalúa expresiones. Sigue leyendo. Prometo hacer esto sucinto.

Necesitas saber algunos hechos básicos sobre 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 se 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 expresión S se evalúa aproximadamente de la siguiente manera:

  1. Si se cita, devolverlo no evaluado
  2. Si no se cita, obtenga su CAR (por ejemplo, primer elemento) y evalúe usando las siguientes reglas:

    a. si es un átomo, simplemente devuelve su valor (por ejemplo, 3 -> 3, "pablo" -> "pablo")
    segundo. si es una expresión S, evalúe usando el mismo procedimiento general
    do. si es un símbolo, devuelve el contenido de su celda de función

  3. Evalúa cada uno de los elementos en la CDR de la expresión S (por ejemplo, todos menos el primer elemento de la lista).

  4. Aplicar 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 una expresión S UNQUOTED debe tener una definición funcional válida en su celda de función.

Ahora, volvamos al ejemplo del comienzo de la publicación. Por que

(#''(lambda (x) (+ x 10)) 20)

generar 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, una forma lambda se evalúa a sí misma, una forma funcional, que es válida como la CAR de una lista UNQUOTED . Cuando Lisp evalúa el CAR de

((lambda (x) (+ x 10)) 20)

se obtiene (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 sea 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 lo tanto, cuándo citar funciones o símbolos que contienen 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. Vea los siguientes ejemplos para obtener una mejor comprensión:

EJEMPLO 1 - Funciones almacenadas en celdas de valor
Supongamos que necesita usar "aplicar" con una función que espera una cantidad 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 +. Puede asignar un valor a su celda de valor si le gusta usar

(setq + "I am the plus function").

Si evalúas

+ -> "I am the plus function"

Sin embargo, (+ 1 2) todavía funciona como se esperaba.

(+ 1 2) -> 3

La función aplicar es bastante útil en la recursión. Supongamos que quiere sumar todos los elementos en una lista. No puedes 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 alguna manera de usar + sin comillas con aplicar? Sí hay. Puedes simplemente evaluar el siguiente código:

(setq + (symbol-function ''+)) (apply + ''(1 2 3))

Esto evaluará a 6, como se esperaba, ya que cuando Lisp evalúa (aplicar + ''(1 2 3)) ahora encuentra la definición funcional de + almacenado en la celda de valor de +.

EJEMPLO 2: Almacenamiento de definiciones funcionales en celdas de valor
Supongamos que almacena una definición funcional en la celda de valor del símbolo. Esto se logra de la siguiente manera:

(setq AFunc (lambda (x) (* 10 x)))

Evaluar

(AFunc 2)

genera un error porque Lisp no puede encontrar una función en la celda de función de 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 almacenada en la celda de valor del símbolo es válida,

(funcall AFunc 2) -> 20

Puede evitar el uso de funcall colocando el formulario lambda en la celda de función del símbolo usando 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 está escribiendo una función y necesita una función que no se usará en ningún otro lado. 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 regresará

(1 4 9)

La razón por la cual Square no se cita en el ejemplo anterior es que la expresión S se evalúa de acuerdo con las reglas que describí 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 celdas de función
Aquí hay un caso cuando se requieren cotizaciones.

(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 evaluará a

"Symbol''s value -> Symbol''s Value" "Symbol''s function -> (lambda nil Symbol''s Function)" nil

Se requieren presupuestos en ambos

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

Me gustaría saber por qué la mayoría del código Common Lisp que veo tiene cosas como

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

en lugar de simplemente

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

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

Editar: Sé que necesitas # ''con los nombres de las funciones porque viven en un espacio de nombres diferente 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 ...


#''foo es la abbreviation de (function foo) del lector.

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

Puede buscar "Lisp-1 vs. Lisp-2" , consultar otras preguntas sobre o leer un artículo anterior en Pitman y Gabriel para aprender más sobre el concepto de múltiples espacios de nombres (también llamados slots o celdas de símbolos) )

La razón por la que, en el caso de lambda, el #'' puede omitirse es que se trata de una macro en CL, que se expande de este modo (tomada del Hyperspec ):

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

#'' todavía se puede usar 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 puedes pensarlo así: (function (lambda ...)) devuelve la función (lambda ...) crea. Tenga en cuenta que lambda en CL Hyperspec tiene una macro Y una entrada de símbolo . De este último:

Una expresión lambda es una lista que se puede usar en lugar de un nombre de función en ciertos contextos para denotar una función al describir directamente su comportamiento en lugar de referirse indirectamente al nombre de una función establecida.

De la documentation de la function :

Si name es una expresión lambda, se devuelve un cierre léxico.

Creo que la diferencia también está relacionada con llamar a formularios lambda como este: ((lambda ...) ...) donde se trata como una forma de ser evaluada, frente a (funcall #''(lambda ...) ...) . Si desea leer más sobre el tema, hay un hilo cll al respecto.

Algunas citas de ese hilo:

(lambda (x) ... por sí solo es una estructura de lista sin comillas. Es su apariencia como un argumento para la forma especial FUNCTION (function (lambda (x) ... que hace que el objeto de función exista

y:

También se ve agravado por el hecho de que la macro LAMBDA era una adición bastante tardía, el ANSI Common Lisp, por lo que todos los tipos muy viejos (es decir, como yo) aprendieron su ceceo cuando necesitabas suministrar la expresión # ''a la lambda en el funciones de mapeo De lo contrario, se invocaría la función lambda no existente.

La adición macro cambió eso, pero algunos de nosotros estamos demasiado dispuestos a querer cambiar.