Eu tenho visto progn
bastante uso enquanto navego nos arquivos de configuração de usuários experientes do Emacs. Encontrei essa boa explicação deprogn
, mas o que realmente me interessa é: qual é o benefício de usar essa função? Tomemos, por exemplo, este trecho (retirado da configuração do 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)))
Existe alguma diferença importante entre a configuração acima e essa?
(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))
Eu sinto que o primeiro exemplo é de alguma forma mais limpo, embora tenha mais sintaxe, e minha intuição é que pode haver algum tipo de aumento de desempenho no uso progn
, mas não tenho certeza. Obrigado por qualquer insight!
use-package
envolverá osprogn
formulários: config se ele estiver ausente. Experimente: você pode colocar um ponto no final de um(use-package ...)
e ligarM-x pp-macroexpand-last-sexp
para ver como a macro é expandida. Você verá que é idêntico para esses dois exemplos.progn
é necessário: emacs.stackexchange.com/questions/39172/…Respostas:
progn
é normalmente usado ao lidar com macros. Algumas macros (use-package
uma macro, verifiquei pela última vez) aceitam apenas 1 formulário , enquanto outras consomem todos os formulários restantes .progn
é usado no primeiro caso para transformar uma sequência de formulários em um único formulário.Nos seus exemplos, o primeiro usa
progn
e, portanto, existe 1 formulário depois:config
. No segundo, existem três formas . Se ause-package
macro espera apenas 1 formulário a seguir:config
, isso causará um erro.Vale ressaltar que o uso de
progn
obras nos dois casos, enquanto a omissão só funciona se a macro aceitar vários formulários. Como resultado disso, algumas pessoas preferem simplesmente usar sempreprogn
, porque sempre funcionará.fonte
progn
", o que significa que aceitam qualquer número de sexps como argumentos separados e os avaliam em sequência. Outros não, e esperam / permitem apenas um único argumento em que você pode avaliar vários sexps em sequência. Isso é tudoprogn
: permite fornecer um único sexp que, quando avaliado, avalia os argumentos do sexpprogn
em sequência. Compare(if true (progn a b c))
com(when true a b c)
. Em ambos os casos,a
,b
, ec
são avaliadas em sequência.use-package
permite múltiplos formulários em:config
e:init
.progn
sexp que contenha vários sexps avaliados por seus efeitos colaterais, antes de retornar o resultado do último deles. Realmente não tem nada a ver com macros. Macros "como exemplo" está bem. Dar a impressão de queprogn
existe para macros e que 99% de seu uso é para macros é enganoso, IMO.progn
trata-se de colocar uma sequência de avaliações em um único sexp (para ajustar-se a um dado argumento esperado) e, portanto, trata-se de efeitos colaterais .progn
foi introduzido no Lisp 1.5 (consulte a seção O Recurso do Programa ), em 1962, muito antes de as macros serem adicionadas ao Lisp. O objetivo é avaliar sequencialmente expressões para seus efeitos colaterais. 2. Vejaprogn
como o próprio Emacs apresentaprogn
aos programadores do Elisp. Veja ozap-to-char
código para um caso de uso simples.progn
tem nada a ver com macros. É claro que seu objetivo é " passar várias formas como uma única forma " (suas palavras) - seja para uma função ou macro ou uma forma especial, mas também para " Eval BODY forms sequencialmente e retornar valor do último " (doc string ) Agora como sempre ("nos dias de hoje", de fato!). Uma avaliação ordenada é importante apenas para efeitos colaterais, obviamente. Um determinado caso de uso (macro ou não) pode ou não se importar com a ordem de avaliação, mas isso é irrelevante. E o Emacs Lisp não é excepcional de forma alguma a esse respeito.O motivo mais importante para o progn é descrito na primeira linha da documentação do progn (ênfase adicionada):
Termo aditivo:
Sem progn , a sequência não é garantida, especialmente se expressões subsequentes dependem dos efeitos colaterais ou retornam valores das expressões anteriores. progn força a sequência de execução da mesma forma que a sequência textual. Ajuda a não confundir execução com análise. Esse comportamento remonta aos fundamentos das estruturas de controle lisp e da programação funcional. Aqui está um trecho (ênfase adicionada) do manual de referência lisp :
O progn melhora o desempenho?
Análise de desempenho, não. Desempenho de execução, não. Na melhor das hipóteses, pode ser igual, mas nunca aumentar magicamente o desempenho.
Quando o progn é usado ?
fonte
if
), mas a ordem de avaliação normal (por exemplo, formulários de nível superior, corpos funcionais etc.) é textual. Claro, até certo ponto isso é implícitoprogn
, mas geralmente não éprogn
para isso que você usa em seu próprio código.(elisp) Sequencing
qual você vincula diz tudo.Uma maneira melhor de entender o que
progn
é é comparando-o com a família:prog1
eprog2
. An
ou1
ou2
parte do nome significa a afirmação da lista cujo resultado você está interessado. Em outras palavras,progn
irá devolver o resultado da última declaração que contém, enquanto queprog1
irá retornar o primeiro, e semelhante paraprog2
.Hoje, essa funcionalidade parece um pouco estranha, pois aprendemos a esperar que o programa retorne na última instrução ou a instruí-lo explicitamente sobre o que retornar. Assim
prog1
eprog2
raramente são usados. No entanto, se você pensar sobre isso, faz sentido, e aqui está como:Diferentes linguagens de programação usam estratégias diferentes para descrever sua semântica. A família Lisp costumava estar intimamente ligada à semântica denotacional. Sem entrar em muitos detalhes, esse tipo de semântica tem uma dificuldade particular com algo que passamos a conhecer como "declarações", que não são "expressões". Os significados do código são tipicamente pensados em termos de combinações de funções, enquanto uma "declaração" que nem sequer pode ser descrita como uma função (já que não avalia nada) é, portanto, difícil de lidar. No entanto, como o Lisp permite efeitos colaterais, algumas vezes um programador gostaria de usar uma expressão sem usar seu valor de uma maneira que não afete a expressão que a segue. E é aí que a
progX
família entra.Em idiomas do tipo C, esse recurso às vezes é conhecido como ponto de sequência ( por exemplo,
;
e,
por exemplo).progn
é tão essencial para idiomas do tipo Lisp quanto;
para idiomas do tipo C. É uma característica fundamental do idioma que não se pode substituir facilmente. No entanto, diferentemente das linguagens de estilo C, o Lisp tende a criar abstrações sintáticas ao ocultar completamente a sintaxe do nível inferior. E talvez por isso você não sejaprogn
usado com tanta frequência, ainda assim, é um dos blocos de construção importantes quando se trata de criar abstrações de linguagem de nível superior (em macros).fonte