什么时候/为什么应该使用progn?


19

progn浏览经验丰富的Emacs用户的配置文件时,已经看到了很多使用情况。我找到了这个很好的解释progn,但是我真正好奇的是,使用此功能的好处是什么?以以下片段为例(摘自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)))

上面的配置与此之间有什么主要区别吗?

(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))

我觉得第一个示例在某种程度上来说更干净,尽管它具有更多的语法,并且我的直觉是使用可能会带来某种性能提升progn,但是我不确定。感谢您的任何见解!


7
在这种特殊情况下,没有什么区别:如果缺少:config,则会在其周围use-package包裹一个progn。尝试一下:您可以将指针放在a的末尾(use-package ...)并调用M-x pp-macroexpand-last-sexp以查看宏的扩展方式。您将看到这两个示例是相同的。
glucas 2015年

Answers:


11

progn通常在处理宏时使用。有些宏(use-package是宏,我最后检查过)接受一种形式,而其他一些则使用所有剩余的形式

progn 在前一种情况下,用于将一系列表格转换为单一表格。

在你的例子,第一个用途progn,因此有1个表格:config。在第二种中,有3种形式。如果use-package宏仅期望1形式跟随:config,则将导致错误。

值得注意的是,progn在两种情况下都可以使用作品,而省略则仅在宏接受多种形式时才有效。因此,有些人只喜欢始终使用progn,因为它将始终有效。


15
它确实与宏无关。一些函数和宏具有所谓的“隐式progn”,这意味着它们接受任意数量的sexp作为单独的参数并按顺序对其求值。其他人则没有,而是只希望/只允许使用一个参数,在此您可能希望按顺序评估多个六次性行为。就是所有的事情progn:它允许您提供一个sexp,在评估时progn按顺序评估sexp args 。(if true (progn a b c))与比较(when true a b c)。在这两种情况下,ab,和c按顺序进行评估。
提请


4
我不同意99%的用例用于宏等。在返回最后一个结果之前,可以向任何函数或宏传递一个prognsexp,该sexp包含评估其副作用的多个sexp。它确实与宏无关。宏“作为一个例子”是可以的。prognIMO 给人的印象是存在的,并且有99%的用途是用于宏。progn关于将一系列评估推入单个 sexp中(以适合给定的预期参数),因此它涉及副作用
提请

5
1. progn在引入的Lisp 1.5(参见节目特征),1962年,前加入到Lisp的宏。目的是顺序评估表达式的副作用。2. progn了解Emacs本身如何介绍progn给Elisp程序员。有关zap-to-char简单的用例,请参见代码。
提请

2
我们可以同意不同意。我的观点是,progn毫无关系与宏。它的目的当然是“ 将多种形式作为单一形式传递 ”(您的单词)-无论是传递给函数,宏还是特殊形式,而且还传递给“ 依序定义EVAL BODY并返回最后一个形式的值 ”(文档字符串) )。现在像以前一样(“这些日子”,的确!)。显然,有序评估仅对副作用很重要。给定的用例(无论是否宏)可能或可能不关心评估顺序,但这无关紧要。在这方面,Emacs Lisp在任何方面都不例外。
提请

10

关于progn的最重要原因在progn文档的第一行中进行了说明(强调):

progn是一种特殊形式,它导致按顺序评估其每个参数,然后返回最后一个参数的值。

附录:

如果没有progn,则不能保证顺序,特别是如果后续表达式依赖于副作用或先前表达式的返回值时。progn强制执行顺序与文本顺序相同。有助于不使执行与解析混淆。此行为可以追溯到Lisp控制结构和功能编程的基础。这是lisp参考手册的摘录(添加了重点):

内置控制结构是特殊形式,因为它们的子形式不必一定要评估或不按顺序评估

是否progn这个提升性能?

解析性能,否。执行性能,否。充其量它可能等于,但绝不能神奇地提高性能。

什么时候 使用progn

...最常在unwind-protect和and或 or的if部分中。


我不确定为什么这么重要。Emacs始终按顺序进行评估。
lunaryorn 2015年

2
@lunaryorn查看更新后的答案。
Emacs用户

我不确定我是否理解您的修改。自然地,存在具有不同评估顺序的特殊形式(例如if),但是正常评估顺序(例如顶级形式,功能主体等)是文本形式的。当然,在某种程度上这是一个隐式progn,但这通常不是您progn 在自己的代码中使用的内容。
lunaryorn 2015年

我认为(elisp) Sequencing您链接的Elisp手动节点说明了一切。
罗勒

10

理解是什么的更好的方法progn是将其与家人进行比较:prog1prog2。名称的n12部分代表列表中您感兴趣的语句的语句。换句话说,progn将返回其包含的最后一条语句的结果,而prog1将返回第一个语句,与相似prog2

今天,此功能似乎有些尴尬,因为我们学会了期望程序在最后一条语句中返回,或者明确指示其返回什么。因此prog1prog2很少使用。但是,如果您考虑一下,这是有道理的,方法如下:

不同的编程语言使用不同的策略来描述其语义。Lisp家族曾经与指称语义紧密相关。无需赘述,这种语义在我们逐渐被称为“语句”(不是“表达式”)的情况下特别困难。代码的含义通常从功能组合的角度来考虑,因此很难处理甚至无法描述为功能的“声明”(因为它不会对任何东西求值)。但是,由于Lisp允许产生副作用,因此有时程序员希望使用表达式而不使用其值,而不会影响其后的表达式。这就是progX家人进来的地方。

在类似C的语言中,此功能有时称为序列点(即;,例如)。 progn对于像Lisp的语言和;对C的语言一样重要。这是一种无法轻易替代的语言的基本特征。但是,与C样式语言不同,Lisp倾向于通过完全隐藏较低层级的语法来构建语法抽象。这也许就是为什么您不progn经常使用它的原因,但是它是构建高级语言抽象(sa宏)时的重要构建块之一。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.