Buscar..


¿Cuándo es necesaria la agrupación?

En algunos lugares de Common Lisp, una serie de formularios se evalúan en orden. Por ejemplo, en el cuerpo de un defun o lambda , o el cuerpo de un dotimes . En esos casos, escribir múltiples formularios en orden funciona como se espera. Sin embargo, en algunos lugares, como las partes then y else de las expresiones if , solo se permite una única forma. Por supuesto, uno puede querer evaluar múltiples expresiones en esos lugares. Para esas situaciones, se necesita algún tipo de forma implícita de agrupación explícita.

Progreso

El operador especial de propósito general progn se utiliza para la evaluación de cero o más formas. Se devuelve el valor del último formulario. Por ejemplo, en el siguiente, se evalúa (print 'hola) (y su resultado es ignorado), y luego se evalúa 42 y (42) se devuelve su resultado:

(progn 
  (print 'hello)
  42)
;=> 42

Si no hay formularios dentro del programa , se devuelve nil :

(progn)
;=> NIL

Además de agrupar una serie de formularios, progn también tiene la importante propiedad de que si el formulario progn es un formulario de nivel superior , todos los formularios dentro de él se procesan como formularios de nivel superior. Esto puede ser importante cuando se escriben macros que se expanden en múltiples formularios y todos deben procesarse como formularios de nivel superior.

Progn también es valioso ya que devuelve todos los valores del último formulario. Por ejemplo,

(progn
  (print 'hello)
  (values 1 2 3))
;;=> 1, 2, 3

En contraste, algunas expresiones de agrupación solo devuelven el valor primario de la forma que produce el resultado.

Avances implícitos

Algunas formas usan programas implícitos para describir su comportamiento. Por ejemplo, el cuándo y menos macros, que son esencialmente de un solo lado, si formas, describen su comportamiento en términos de un progn implícita. Esto significa que una forma como

(when (foo-p foo)
  form1
  form2)

se evalúa y la condición (foo-p foo) es verdadera, entonces form1 y form2 se agrupan como si estuvieran contenidos dentro de un progn . La expansión de la macro cuando es esencialmente:

(if (foo-p foo)
  (progn
    form1
    form2)
  nil)

Prog1 y Prog2

Muchas veces, es útil evaluar múltiples expresiones y devolver el resultado de la primera o segunda forma en lugar de la última. Esto es fácil de lograr usando let y, por ejemplo:

(let ((form1-result form1))
  form2
  form3
  ;; ...
  form-n-1
  form-n
  form1-result)

Debido a que esta forma es común en algunas aplicaciones, Common Lisp incluye prog1 y prog2 que son como progn , pero devuelven el resultado de la primera y la segunda forma, respectivamente. Por ejemplo:

(prog1
  42
  (print 'hello)
  (print 'goodbye))
;; => 42
(prog2
  (print 'hello)
  42
  (print 'goodbye))
;; => 42

Sin embargo, una distinción importante entre prog1 / prog2 y progn es que progn devuelve todos los valores de la última forma, mientras que prog1 y prog2 solo devuelven el valor primario de la primera y la segunda forma. Por ejemplo:

(progn
  (print 'hello)
  (values 1 2 3))
;;=> 1, 2, 3

(prog1
  (values 1 2 3)
  (print 'hello))
;;=> 1              ; not 1, 2, 3

Para valores múltiples con una evaluación de estilo prog1 , use multiples valor-prog1 en su lugar. No existe un prog2 de valor múltiple similar, pero no es difícil de implementar si lo necesita.

Bloquear

El especial operador bloque permite agrupación de varias formas Lisp (como un implícito progn ) y también toma un nombre para nombrar el bloque. Cuando se evalúan los formularios dentro del bloque, el operador especial de devolución puede usarse para abandonar el bloque. Por ejemplo:

(block foo
  (print 'hello)     ; evaluated
  (return-from foo)
  (print 'goodbye))  ; not evaluated
;;=> NIL

return-from también se puede proporcionar con un valor de retorno:

(block foo
  (print 'hello)     ; evaluated
  (return-from foo 42)
  (print 'goodbye))  ; not evaluated
;;=> 42

Los bloques con nombre son útiles cuando una porción de código tiene un nombre significativo, o cuando los bloques están anidados. En algún contexto, solo la capacidad de regresar de un bloque temprano es importante. En ese caso, puede usar nil como nombre de bloque y volver . La devolución es igual que la devolución , excepto que el nombre del bloque siempre es nulo .

Nota: los formularios adjuntos no son formularios de nivel superior. Eso es diferente de progn , donde las formas adjuntas de una forma de progn nivel progn todavía se consideran formas de nivel superior .

Tagbody

Para una gran cantidad de control en los formularios de un grupo, el operador especial de tagbody puede ser muy útil. Los formularios dentro de un formulario de tagbody son etiquetas go (que son solo símbolos o enteros) o formularios para ejecutar. Dentro de un tagbody , el operador especial go se utiliza para transferir la ejecución a una nueva ubicación. Este tipo de programación puede considerarse de nivel bastante bajo, ya que permite rutas de ejecución arbitrarias. El siguiente es un ejemplo detallado de cómo se vería un for-loop cuando se implementa como un cuerpo de etiqueta :

(let (x)               ; for (x = 0; x < 5; x++) { print(hello); }
  (tagbody
     (setq x 0)
   prologue
     (unless (< x 5)
       (go end))
   begin
     (print (list 'hello x))
   epilogue
     (incf x)
     (go prologue)
   end))

Mientras que Tagbody and Go no se usa comúnmente, tal vez debido a que "GOTO se considera dañino", pero puede ser útil al implementar estructuras de control complejas como las máquinas de estado. Muchas construcciones de iteración también se expanden en un cuerpo de etiqueta implícito . Por ejemplo, el cuerpo de un punto se especifica como una serie de etiquetas y formas.

¿Qué forma utilizar?

Cuando se escriben macros que se expanden en formas que podrían involucrar agrupación, vale la pena dedicar un tiempo a considerar en qué agrupación se expandirá la construcción.

Para las formas de estilo definición, por ejemplo, un define-widget de macro que generalmente aparecerá como un formulario de nivel superior, y que varios defun s, defstruct s, etc., por lo general tiene sentido utilizar un progn, de modo que los formularios secundarios son Procesados ​​como formularios de nivel superior. Para las formas de iteración, un tagbody implícito es más común.

Por ejemplo, el cuerpo de dotimes , dolist , y hacer cada expanden en un tagbody implícita.

Para los formularios que definen una "parte" del código, un bloque implícito suele ser útil. Por ejemplo, mientras que el cuerpo de un defun está dentro de un progn implícito, ese progn implícito está dentro de un bloque que comparte el nombre de la función. Eso significa que se puede utilizar return-from para salir de la función. Tal comp



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow