为什么在基于Lisp的语言中,习惯在一行上放置许多闭合括号?

40

通常代码看起来像这样:

(one-thing
    (another-thing arg1 (f arg5 r))
    (another-thing arg1 (f arg5 r)))

为什么它不喜欢这样写?

(one-thing
    (another-thing arg1 (f arg5 r))
    (another-thing arg1 (f arg5 r))
)

这使得添加和删除"another-thing"行更加容易(无需删除和重新添加尾随的闭合括号)。此外,您可以在该孤立的闭合括号上放置一些注释(如"; loop结束")。

当我将使用第二种样式的代码与现有使用第一种样式的代码混合使用时,情况有多糟糕?


7
我个人非常喜欢第二种风格。在阅读有关编写干净代码的书籍和文章后,花费额外的努力去了解“某些东西”结束的位置(并不是它只是结束了),这是可以接受的。 - Belun
我过去习惯于在同一行的右括号和前几行的左括号之间放一个空格。不,当时我的编辑器没有突出显示匹配的括号。 - starblue
可能是Lisp括号的重复问题。 - Mechanical snail
8个回答

50
在Lisp和其他使用S表达式语法的语言中,括号主要是为了编译器而存在的,而布局和缩进(编译器忽略这些)则是为了程序员的利益。
因此,没有必要将闭合括号放在它们自己的行上:良好选择的换行和缩进足以使结构清晰。
例如,
(defun clone-indirect-buffer-other-window (newname display-flag &optional norecord)
  "Like `clone-indirect-buffer' but display in another window."
  (interactive
   (progn
     (if (get major-mode 'no-clone-indirect)
         (error "Cannot indirectly clone a buffer in %s mode" mode-name))
     (list (if current-prefix-arg
               (read-buffer "Name of indirect buffer: " (current-buffer)))
           t)))
  (let ((pop-up-windows t))
    (clone-indirect-buffer newname display-flag norecord)))

缩进可以清晰地显示结构(对于有一定经验的Lisp程序员)。将一些闭合括号移到新行上不会增加任何内容。
(defun clone-indirect-buffer-other-window (newname display-flag &optional norecord)
  "Like `clone-indirect-buffer' but display in another window."
  (interactive
   (progn
     (if (get major-mode 'no-clone-indirect)
         (error "Cannot indirectly clone a buffer in %s mode" mode-name)
       )
     (list (if current-prefix-arg
               (read-buffer "Name of indirect buffer: " (current-buffer))
             )
           t)
     )
   )
  (let ((pop-up-windows t))
    (clone-indirect-buffer newname display-flag norecord)
    )
  )

我应该补充一点,几乎所有的Lisp程序员都使用一个编辑器来显示匹配的括号、执行自动缩进,并提供一个用户界面来直接处理平衡表达式。例如,在Emacs中,有M-(用于插入新表达式,M-)用于移动到当前表达式的末尾,C-M-k用于删除点后的表达式等等。
因此,Lisp程序员不需要手动计算括号以确定哪些括号匹配。
Taylor R. Campbell生动地阐述了这个理念:

实际的括号字符只是词法标记,应给予很少的意义。Lisp程序员不会单独检查括号,更不用说数括号了;相反,他们查看程序中表达的高级结构,特别是通过缩进展示的结构。 Lisp不是关于编写一系列串行指令的;它是关于通过总结部分来构建复杂结构的。从部分中组合复杂结构是Lisp程序的重点,并且它应该从Lisp代码中很容易地看出来。随意地放置括号会对Lisp程序员造成冲击,否则他们大部分时间甚至不会看到它们。


3
确实,人们避免将括号单独放在一行的一个好理由是这样可以更容易地进行计数,而计算括号数量是人类绝对不应该做的事情。 - user61051
2
“并提供用户界面直接处理平衡表达式”,这部分在我的 Vim 中尚未配置,这也是我考虑将一些闭合括号放在单独的行上的主要原因。 - Vi.
如果你正在使用vim,可以执行命令":set showmatch",然后当你把光标放在括号上时,它会显示匹配的括号。 - coder_tim

30

这里有两点需要说明:

  1. 惯例本身就很重要。使用括号包裹的风格让其他Lisp程序员更易读你的代码,如果你采用这种风格,也会习惯阅读他们的代码。

  2. )分行并不是大多数Lisper认为的优点。如果你使用一款半靠谱的编辑器,它会有命令来理解平衡表达式,以便移动、剪切、粘贴、转置等操作。因此你不需要这样做。

)  ; end of the loop

在Lisp中,你只需要所需的东西。

# end of the loop
  • 在一些像Python这样对空格敏感的语言中

  • 例如,请参见http://www.gnu.org/software/emacs/manual/html_node/emacs/Expressions.html


    9
    他们不太是Lisp程序员。但现在剩下的“ed”和“edlin”用户不多了,对吧?Vi(m)用户,看看https://dev59.com/qnVD5IYBdhLWcg3wE3bz - telent
    FYI:这是Python的注释字符 - Paul Nathan
    2
    我不能说我同意第一条理由,或者它被列为第一条。这让人觉得像是Lisp程序员主要之所以使用这种风格是因为其他所有Lisp程序员都这么做。 :-) 实际上,第二个原因才是真正的原因:它节省了大量的空间,而那些在同一行使用“))”更困难的(非常)少数事情,我们通过将该功能添加到我们的编辑器中来解决(部分原因是Lisp对于它来说很容易解析)。 - Ken
    3
    规范本身就很重要。...那么也许我们都应该用Java编程。您并没有陈述任何有关传统风格的论据,只是说它是常规的,传统喜欢它...而且它需要一个特殊的编辑器才能使用(这是对它的一个缺点)。 - Sam
    在我看来,“诉诸传统”从来不是任何讨论中的有效论据。https://0cn.de/rq3e - Mecki
    显示剩余3条评论

    10

    5
    我认为这并没有回答问题。坚持传统可能是有利的,但这并不能说明任何人为什么会选择这个特定的习惯。曾经有人必须首先以这种风格写作,并且(通过共识、权威或其他原因)使其成为“公认的标准”。 - Ken

    3
    如果您正在使用Emacs,您需要学习这些。特别是,C-M-k使得杀死一个平衡表达式和杀死一行一样容易。再加上良好的平衡括号高亮显示,就真的没有必要这样写了。
    另一方面,将所有这些)单独放在一行上意味着您在屏幕上看到的代码更少,使得阅读和重构变得更加困难。
    如果您没有使用Emacs,您至少应该使用支持这些基本操作的编辑器,否则编写lisp代码将会很痛苦。

    1
    目前我通常采用平衡的方法:根据上下文,在同一行放置一些闭合括号,有些缩进到下一行,有些缩进到第三行。在考虑应该在哪一行放置多少闭合括号时,我通常会思考以下两点:1. 重复/删除行是否有意义?2. RCS差异和补丁是否看起来好?此外,当我积极开发代码时,我倾向于留下许多打断行的单个 ')',然后在代码稳定后将其“重构”为上述方案。 - Vi.
    我没有考虑到修订控制差异的影响。仅仅因为一个括号而导致的行数变化有点烦人。我知道git可以忽略空格的更改,但我想知道它是否可以配置为忽略单个括号的更改。 - asmeurer

    3

    做你喜欢的事情!这是你的代码。

    话虽如此,你可能最终会把它们全部移回来,以便一次性在屏幕上获得更多的内容。事实上,你会发现过一段时间后,你会有效地停止看到括号。

    实际上,情况比这还要糟糕一些。现在当我尝试使用心爱的Python时,感觉没有了括号,我的代码就不再安全地连接在一起,我担心它随时可能崩溃。

    甚至可以记录几个键盘宏来切换整个文件的风格。 (并学习如何使您的版本控制忽略仅限于空格的更改。:-)


    当我开始写Python时,也感觉我的代码会崩溃。我记得在空白行上放置 # 字符来模拟闭合括号... 哈哈 - byxor

    1
    我认为一个好的回答是提出一个相关的问题:
    为什么Python程序员不把紧密嵌套的字符放在自己的行上?
    现在的答案很明显:没有这样的字符。事实证明,Lisp中也没有这样的字符:是的,Lisp阅读器需要这些略微烦人的(和)字符,但人类并不像这样阅读Lisp代码:他们通过缩进和单词来阅读它。

    这些尾括号也会出现在差异中,在 Python 情况下该行不会被更改。 - Vi.

    1

    节省垂直屏幕(和眼睛)的空间非常重要。


    1
    括号并不是为了可读性而存在,它们是为了计算机而存在的。程序员可以通过查看缩进来确定代码块的结束位置。此外,多个关闭括号共享一行意味着程序员可以在屏幕上同时查看更多的代码。

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接