He visto que progn
se usa mucho mientras navego por los archivos de configuración de usuarios experimentados de Emacs. Encontré esta buena explicación deprogn
, pero lo que realmente me interesa es, ¿cuál es el beneficio de usar esta función? Tomemos, por ejemplo, este fragmento (tomado de la configuración de Sacha Chua ):
(use-package undo-tree
:defer t
:ensure t
:diminish undo-tree-mode
:config
(progn
(global-undo-tree-mode)
(setq undo-tree-visualizer-timestamps t)
(setq undo-tree-visualizer-diff t)))
¿Hay alguna diferencia importante entre la configuración anterior y esta?
(use-package undo-tree
:defer t
:ensure t
:diminish undo-tree-mode
:config
(global-undo-tree-mode)
(setq undo-tree-visualizer-timestamps t)
(setq undo-tree-visualizer-diff t))
Siento que el primer ejemplo es de alguna manera más limpio, a pesar de que tiene más sintaxis, y mi intuición es que podría haber algún tipo de aumento de rendimiento por el uso progn
, pero no estoy seguro. Gracias por cualquier idea!
use-package
envolverá unprogn
alrededor de sus formularios: config si falta. Pruébelo: puede poner el punto al final de una(use-package ...)
y llamarM-x pp-macroexpand-last-sexp
para ver cómo se expande la macro. Verá que es idéntico para estos dos ejemplos.progn
se necesita: emacs.stackexchange.com/questions/39172/…Respuestas:
progn
se usa típicamente cuando se trata de macros. Algunas macros (use-package
es una macro, la última que verifiqué) aceptan solo 1 formulario , mientras que otros consumen todos los formularios restantes .progn
se usa en el primer caso para convertir una secuencia de formas en una sola forma.En sus ejemplos, el primero usa
progn
y, por lo tanto, hay 1 formulario después:config
. En el segundo, hay 3 formas . Si lause-package
macro solo espera 1 formulario siguiente:config
, causará un error.Vale la pena señalar que el uso
progn
funciona en ambos casos, mientras que omitirlo solo funciona si la macro acepta múltiples formas. Como resultado de esto, algunas personas prefieren simplemente usar siempreprogn
, porque siempre funcionará.fuente
progn
", lo que significa que aceptan cualquier número de sexps como argumentos separados y los evalúan en secuencia. Otros no lo hacen, y en cambio esperan / permiten solo un único argumento en el que es posible que desee evaluar múltiples sexps en secuencia. Eso es todoprogn
: le permite proporcionar un único sexp que, cuando se evalúa, evalúa los argumentos de sexpprogn
en secuencia. Comparar(if true (progn a b c))
con(when true a b c)
. En ambos casos,a
,b
, yc
son evaluados de forma secuencial.use-package
permite múltiples formas en:config
y:init
.progn
sexp que contiene múltiples sexps que se evalúan por sus efectos secundarios, antes de devolver el resultado del último de estos. Realmente no tiene nada que ver con las macros. Las macros "como ejemplo" están bien. Dar la impresión queprogn
existe para las macros, y que el 99% de su uso es para macros, es engañoso, en mi opinión.progn
se trata de palear una secuencia de evaluaciones en un solo sexp (para ajustarse a un argumento esperado), y por lo tanto se trata de efectos secundarios .progn
se introdujo en Lisp 1.5 (ver sección La característica del programa ), en 1962, mucho antes de que se agregaran macros a Lisp. El propósito es evaluar secuencialmente las expresiones para sus efectos secundarios. 2. Veaprogn
cómo Emacs se presentaprogn
a los programadores de Elisp. Vea elzap-to-char
código para un caso de uso simple.progn
tiene nada que ver con las macros. Su propósito es, por supuesto, " pasar múltiples formas como una sola forma " (sus palabras), ya sea a una función o una macro o una forma especial, pero también a " Evaluar las formas del CUERPO secuencialmente y devolver el valor de la última " (cadena de documentación ) Ahora como siempre ("estos días", de hecho!). Una evaluación ordenada es importante solo para los efectos secundarios, obviamente. Un caso de uso dado (macro o no) puede o no preocuparse por el orden de evaluación, pero eso es irrelevante. Y Emacs Lisp no es excepcional de ninguna manera a este respecto.La razón más importante para el progn se describe en la primera línea de la documentación del progn (énfasis agregado):
Apéndice:
Sin progn , la secuencia no está garantizada, especialmente si las expresiones posteriores dependen de los efectos secundarios o los valores de retorno de las expresiones anteriores. progn impone la secuencia de ejecución igual que la secuencia de texto. Ayuda a no confundir ejecución con análisis. Este comportamiento se remonta a los fundamentos de las estructuras de control de lisp y la programación funcional. Aquí hay un extracto (énfasis agregado) del manual de referencia de lisp :
¿ Progn aumenta el rendimiento?
Análisis de rendimiento, no. Ejecución de ejecución, no. En el mejor de los casos, puede igualar pero nunca aumentar mágicamente el rendimiento.
¿Cuándo se usa progn ?
fuente
if
), pero el orden de evaluación normal (por ejemplo, formas de nivel superior, cuerpos de funciones, etc.) es textual. Claro, hasta cierto punto eso es implícitoprogn
, pero generalmente no es lo que usaprogn
en su propio código.(elisp) Sequencing
lo dice todo.Una mejor manera de entender lo que
progn
es es comparándolo con la familia:prog1
yprog2
. Eln
o1
o2
parte del nombre se refiere a la declaración de la lista cuyo resultado que le interesa. En otras palabras,progn
devolverá el resultado de la última declaración que contiene, mientras queprog1
devolverá el primero, y similar paraprog2
.Hoy en día, esta funcionalidad parece un poco incómoda, ya que aprendemos a esperar que el programa regrese en la última declaración o a indicarle explícitamente qué devolver. Por lo tanto
prog1
yprog2
rara vez se utilizan. Sin embargo, si lo piensa, tiene sentido, y así es cómo:Diferentes lenguajes de programación utilizan diferentes estrategias para describir su semántica. La familia Lisp solía estar estrechamente vinculada a la semántica denotacional. Sin entrar en muchos detalles, este tipo de semántica tiene una dificultad particular con algo que crecimos conocer como "declaraciones", que no son "expresiones". Los significados del código generalmente se piensan en términos de combinaciones de funciones, mientras que una "declaración" que ni siquiera se puede describir como una función (ya que no evalúa nada) es, por lo tanto, difícil de manejar. Sin embargo, dado que Lisp permite efectos secundarios, a veces un programador querría usar una expresión sin usar su valor de manera que no afecte la expresión que le sigue. Y aquí es donde
progX
entra la familia.En lenguajes tipo C, esta característica a veces se conoce como punto de secuencia (es decir,
;
y,
por ejemplo).progn
es tan esencial para los lenguajes de tipo Lisp como lo;
es para los lenguajes de tipo C. Es una característica fundamental del lenguaje que no se puede reemplazar fácilmente. Sin embargo, a diferencia de los lenguajes de estilo C, Lisp tiende a construir abstracciones sintácticas al ocultar por completo la sintaxis del nivel inferior. Y esta es quizás la razón por la que no seprogn
usa con tanta frecuencia, sin embargo, es uno de los bloques de construcción importantes, cuando se trata de construir abstracciones de lenguaje de alto nivel (sa macros).fuente