recursivas listas funciones ejemplos lisp common-lisp

listas - En Common Lisp, ¿por qué los cuerpos de múltiples expresiones de(if) enunciados requieren(progn)?



lisp pdf (9)

¿Es esto simplemente un pedazo de residuo histórico de la década de 1950 o hay alguna razón sintácticamente por qué los cuerpos de múltiples expresiones de (if) formas requieren (progn)? ¿Por qué no puedes envolver las múltiples expresiones en un conjunto de paréntesis como con (let):

(if some-cond ((exp1) (exp2) (exp3)) ; multi exp "then" (exp4)) ; single exp "else"

Parece que sería trivial escribir una macro para probar que cada cuerpo vea primero si es una lista y luego si lo es, si su primer elemento también es una lista (y por lo tanto no una invocación de función) y luego envolver sus subcomponentes. dentro de un (progn) en consecuencia.


¿Hay alguna razón sintácticamente por qué los cuerpos de múltiples expresiones de (if) formas requieren (progn)?

La respuesta es "sí", aunque tal vez no por la razón que está esperando. Como Common Lisp (a diferencia de Scheme y otros Lisps) requiere funcall , su propuesta no es ambigua. Incluso si fuera ambiguo, siempre y cuando los usuarios sepan que los paréntesis implican un progn aquí, funcionaría.

Sin embargo, ninguna otra construcción * en el lenguaje tiene paréntesis simples / dobles opcionales. Muchas construcciones tienen progn implícitos, pero su sintaxis progn es siempre la misma.

Por ejemplo, cond tiene un progn implícito para cada rama:

(cond (test1 body1) (test2 body2) ...)

No puedes cambiar de un lado a otro:

(cond test1 exp1 (test2 body2) t exp3)

Entonces, aunque su propuesta no es ambigua, no se ajusta a la sintaxis del resto del lenguaje. ¡SIN EMBARGO! Como dijiste, la macro es trivial de implementar. Deberías hacerlo tú mismo y ver si funciona bien. Fácilmente podría estar equivocado; Soy bastante parcial ya que casi todo mi Lisping está en Scheme.

* Excepto el case . Hmf. Ahora creo que puede haber otros.


Common Lisp no es perfecto porque es perfecto, es perfecto porque es perfectable.

El lenguaje completo se basa en 25 operadores especiales ; if es uno de esos, progn es otro.

if proporciona solo el mecanismo básico para probar una condición, saltando a una u otra dirección de código. progn proporciona el mecanismo básico de hacer varias cosas y devolver el valor de la última.

Hay varias macros en el estándar de lenguaje que se basan en esto, por ejemplo, when , unless , cond , case .

Si lo desea, tiene varias opciones para hacer algo como lo que imagina: para progn , podría escribir una macro ifm que progn implícitos como cláusulas progn y else, o podría escribirla como usted dijo, para que detecta el intento, o incluso puede escribir una macro de lectura para agregar azúcar sintáctica para el progn .


Cuando no tiene una rama ''else'', macros estándar when y a unless que unless ayuda. De lo contrario, es mejor usar cond si tienes múltiples expresiones en cualquier rama.


En Lisp, los paréntesis indican la aplicación de función, no la agrupación. ¿Qué significaría tu expresión si exp1 fuera una función que devolvió una función? ¿Sería llamado con los argumentos (exp2) (exp3) o no?


No todas las expresiones son listas. Para (let ((a 42)) (if some-cond (abc) (def))) no sabría si (abc) debería interpretarse como una llamada a la función a o como un progn implícito.



En Common Lisp, este código:

(if t ((lambda (x) (+ x 5)) 10) 20)

devolverá 15. Con su propuesta, creo que verá que la cláusula verdadera es una lista y la convertirá automáticamente en:

(if t (progn (lambda (x) (+ x 5)) 10) 20)

que regresaría 10. ¿Es eso correcto?

No estoy seguro de que sea "trivial" distinguir entre "lista" y "invocación de función" en CL. ¿Pretende que este cambio no sea compatible con versiones anteriores? (Los nuevos e interesantes dialectos Lisp siempre son geniales, pero no es Common Lisp). ¿O puedes dar un ejemplo de lo que tienes en mente?


Porque la sintaxis para el enlace IF (<- HyperSpec) se define como:

if test-form then-form [else-form] => result*

No hay marcadores de comienzo o final. Hay un THEN-FORM y no THEN-FORM *. PROGN es un mecanismo para definir una secuencia de formularios, donde los formularios se ejecutan de izquierda a derecha y se devuelven los valores de la última forma.

Podría haber sido definido así:

my-if test-form (then-form*) [(else-form*)] => result* (defmacro my-if (test then &optional else) (assert (and (listp then) (listp else)) (then else)) `(if ,test (progn ,@then) (progn ,@else))) (my-if (> (random 10) 5) ((print "high") :high) ((print "low") :low))

Bueno, ya existe una construcción que admite múltiples formas: COND.

(cond ((> (random 10) 5) (print "high") :high) (t (print "low") :low))

El estilo típico es usar COND cuando se deben probar múltiples alternativas y cuando hay múltiples formas then / else. IF se usa cuando hay solo una prueba y ambas formas, then y else. Para otros casos, hay CUÁNDO y MENOS. CUANDO y A MENOS soportar solo uno o ENTONCES formularios (ninguna otra forma (s)).

Supongo que es bueno tener al menos una forma condicional (IF en este caso) que viene sin capas agregadas de paréntesis. Escritura

(if (> (random 10) 5) (progn (print "high") :high) (progn (print "low") :low))

es entonces un pequeño precio a pagar. Escriba los PROGN adicionales o cambie a la variante COND. Si su código realmente se beneficiaría de IF con múltiples formas entonces, entonces simplemente escriba esa macro (vea arriba). Lisp lo tiene, para que puedas ser tu propio diseñador de idiomas. Sin embargo, es importante pensar en introducir una macro: ¿es correcta mi macro? ¿Comprueba errores? ¿vale la pena? ¿es legible (para otros?)?


Tenga en cuenta que en los "{} idiomas" (C, C ++, Java ...) tiene un PROGN en forma de la instrucción compuesta entre llaves.

Así que tenga en cuenta estas equivalencias / analogías:

(if antecedent consequent alternative) if (antecedent) consequent; else alternative; (if antecedent (progn consequent1 consequent2) alternative) if (antecedent) { consequent1; consequent2; } else alternative;

El operador PROGN (y sus primos) son los "refuerzos" de Lisp. ¡Los paréntesis en Lisp no son sus "llaves"!

Ahora considera Perl. Perl tiene un if completamente armado. Esto podría hacerse en Lisp también:

(if antecedent (consequent1 consequent2 ...) (alternative1 alternative2 ...))

P.ej:

(if (< foo 0) ((format t "foo is less than zero") (- foo)) ((format t "foo is not less than zero") foo))

Podría vivir con esto, creo, pero algunas personas se quejarán de los paréntesis adicionales, especialmente en los casos simples.