Pregunta

Estoy leyendo sobre los paradigmas de programación de inteligencia artificial de Peter Norvig, y me he encontrado con un problema que no puedo resolver por mi cuenta (esta es mi introducción a LISP). El problema es bastante pequeño, realmente, pero obviamente no puede resolver mi pequeño cerebro.

¿Por qué es que cuando el valor de una función es una lambda, es un error usar esa función como el primer elemento en una lista? Por ejemplo:

Ceceo:

(defun some-func ()
  #'(lambda (x) x))

;; At REPL
;; Does not work
> ((some-func) 1)
;; Does work
> ((lambda (x) x) 1)
;; Also works
> (funcall (some-func) 1)

¡Espero que tenga sentido!

¿Fue útil?

Solución

Esta es una buena pregunta, y una donde el Lisp común puede ser bastante confuso. La cuestión es que, debido a razones históricas, el LISP común tiene dos espacios de nombres, uno para funciones y otro para valores. Para que esto suceda, hay dos reglas de evaluación diferentes para la posición principal de una aplicación de función y para el resto: el primero evaluará un símbolo como nombre de función, y el segundo evaluará un símbolo como una referencia variable. Obviamente, hay casos en los que el valor es realmente una función, por ejemplo, si escribe un mapcar función, querrás hacer algo como

(defun my-mapcar (f l)
  (if (null l)
    '()
    (cons (f (car l)) (my-mapcar f (cdr l)))))

Pero esto no funcionará, se quejará de f Ser una función desconocida. Para estos casos se llama una función especial llamada funcall, que recibe una función y argumento para la función, y aplicará la función como de costumbre, y desde funcall es una función simple, sus argumentos se evalúan como de costumbre (como valores). Entonces lo anterior debe solucionarse usando:

(defun my-mapcar (f l)
  (if (null l)
    '()
    (cons (funcall f (car l)) (my-mapcar f (cdr l)))))

Como probablemente sospeche ahora, existen los casos de espejo, en los que desea evaluar algo en función y no como un valor. Por ejemplo, esto no funciona:

(my-mapcar 1+ '(1 2 3))

Porque se refiere al 1+ variable y no la función. Para estos casos hay una forma especial llamada function que evalúa su contenido como una función y lo devuelve como un valor:

(my-mapcar (function 1+) '(1 2 3))

y se puede abreviar con #':

(my-mapcar #'1+ '(1 2 3))

Ese no es el final de esta historia, para dar algunos ejemplos:

  • En algunos casos, un nombre simple citado puede funcionar como una función, por ejemplo '1+ En el último ejemplo funciona, pero esta es una especie de truco que solo puede ver los nombres limitados a nivel mundial, por lo tanto #' casi siempre es mejor

  • Se puede usar un truco similar con lambda - para que puedas usar (my-mapcar '(lambda (x) (1+ x)) '(1 2 3)), de hecho, podrías usar (list 'lambda '(x) '(1+ x)) que es aún peor (e iirc, no portátil), pero usando (lambda (x) (1+ x)) funciona ya que está implícitamente envuelto en un #' (Intente expandir un lambda Forma como macro y lo verá). Un truco relacionado deja bien usar un lambda Expresión como la cabeza de una aplicación de función (que es una de las cosas que ha probado).

  • let ETC une los valores locales, y en algunos casos querrá vincular las funciones locales, para esto, existen nuevas construcciones vinculantes: flet y labels

Si todo esto parece extraño y/o demasiado complicado, entonces no estás solo. Es una de las principales diferencias entre el LISP y el esquema comunes. (La diferencia conduce a cambios en modismos comunes en ambos idiomas: el código del esquema tiende a utilizar funciones de orden superior con mucha más frecuencia que el código LISP común. Como de costumbre, con este tipo de preguntas religiosas, algunas personas argumentan a favor de lo que hace CL, afirmando, afirmando que las funciones de orden superior son confusas, por lo que les gusta el recordatorio explícito en el código).

Otros consejos

((lambda (x) ...) ...) es un caso especial codificado en las reglas del evaluador. No está evaluando el primer elemento en el formulario y utilizando el resultado como caso general. Es normal que tengas que usar funcall o apply llamar a una función que es el resultado de evaluar alguna otra forma.

La razón por la que el LISP común no permite que una función que devuelva una lambda sea el primer elemento en una expresión tiene que ver con la distinción entre Lisp-1 y Lisp-2.

Si se permite el lisp común ((some-func) 1) ser equivalente a (funcall (some-func) 1), entonces podría ser percibido como inconsistente no permitir también decir (let ((f #'some-func)) (f 1)) en lugar de requerir (funcall f 1).

En realidad, hay una muy buena justificación para no apoyar tales formas: son ambiguas. Considerar el camino nombres de funciones se definen en LISP común:

nombre de la función norte. 1. (en un ambiente) A símbolo o lista (setf symbol) eso es el nombre de función en eso ambiente. 2. A símbolo o lista (setf symbol).

Dado esto, ¿cómo debería comportarse una forma como la siguiente?

((setf car) 10 x)

Si este conjunto xEl auto al valor 10, o debe ejecutar el formulario (setf car) (que sería un error) e intente usar su valor de retorno en función para llamar con los argumentos 10 y x? Common LISP no especifica ninguno de los comportamientos, y no me queda nada claro que sea una mala elección. Después de todo, por lo que puedo ver, nada en el estándar evita que las implementaciones conformes extendan la definición de formularios válidos para admitir una gama más amplia de nombres de operadores (por lo que las funciones de SETF de case especial tampoco ayudarían aquí).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top