如何格式化lisp代码?

mor*_*ode 2 lisp scheme code-formatting

考虑来自SICP的这个迭代因子过程.

(define (fact-iter product counter max-count)
  (if (> counter max-count)
      product
      (fact-iter (* counter product)
                 (+ counter 1)
                 max-count)))
Run Code Online (Sandbox Code Playgroud)

在这里,我们看到:

  • 阶乘的声明没有前导空格.我认为这是正常的.
  • 函数体需要每行两个前导空格.
  • 我们在if语句的第一个子句和第二个子句的开头添加了四个空格.共有6个空格.
  • 我们在最后两行中添加了11个空格,if语句的第二个子句的其余部分.共17个空格.

为什么会这样?间距让我感到困惑.为什么它不能像java(在代码的每个内部添加四个空格)?我该如何格式化lisp代码?

Ale*_*ing 9

tl; dr:它使表达式的嵌套清晰


Lisps的一个定义特性,包括Scheme,你编写的代码本质上是AST(抽象语法树).像Java这样的语言有很多语法,所以它们需要解析器正确消除歧义可能模糊的语法.中缀运营商就是一个典型的例子.考虑Java中的以下语句:

int x = 1 + y * 2;
Run Code Online (Sandbox Code Playgroud)

代码的文本表示肯定不会暗示任何树形结构,但实际上,这种说法有一个规范的解析实际上是一个树.它看起来像这样:

     =
    / \
int x  +
      / \
     1   *
        / \
       y   2
Run Code Online (Sandbox Code Playgroud)

另一方面,等效的Scheme代码使得所有嵌套非常明确:

(define x (+ 1 (* y 2)))
Run Code Online (Sandbox Code Playgroud)

注意显式分组如何创建一个非常明确定义的表达式树.像大多数其他语言一样,不需要运算符优先级规则.这种简单性是一种有意的设计选择,因为当源代码表示非常简单时,编写转换它的宏非常容易.Lisps倾向于大量使用宏,因为与其他编程语言相比,操纵AST相对轻松,因为语法简单.


考虑到所有这些,缩进规则可能会变得更加明显:Lisp代码通常以这样的方式缩进,以便AST的结构立即可见.考虑fact-iter使用"更简单"缩进样式的示例函数的替代版本:

(define (fact-iter product counter max-count)
  (if (> counter max-count)
    product
    (fact-iter (* counter product)
      (+ counter 1)
      max-count)))
Run Code Online (Sandbox Code Playgroud)

在这种特殊情况下,缩进并不是灾难性的,但fact-iter现在递归调用在视觉上解析起来要困难得多.Lisp/Scheme语法的一致性使得很难立即了解fact-iter使用三个参数调用的事实,因为第一个参数不再与后两个参数对齐.

这可以通过将所有参数放在单独的行上来解决:

(fact-iter
 (* counter product)
 (+ counter 1)
 max-count)
Run Code Online (Sandbox Code Playgroud)

这个工作,实际上是可以接受的Lisp风格.尽管如此,它往往是对垂直空间的重大浪费,并且它仍然使AST更难以立即修复,因为压痕在视觉上显着不那么明显.


对于在Scheme中使用"更简单"缩进模型是灾难性的示例,请考虑以下两个等效表达式:

(string->number (if (string? x) x
                    (format "~a" x)))

(string->number (if (string? x) x
  (format "~a" x)))
Run Code Online (Sandbox Code Playgroud)

第一个例子维护AST.很容易看出format调用是if表单的"else"情况,因为它嵌套在表单下面.第二个例子没有维护AST,乍看之下不清楚调用format是否嵌套在内部,if或者只是传递给第二个参数string->number.你可以看到Lisp的语法并没有真正说清楚.

方案缩进起初看起来有点时髦,但是一旦你习惯了它,它就会使代码更容易看到,而不必在脑子里耍弄括号.语法的一致性既是一种祝福又是一种诅咒:它使得编写宏变得微不足道,但它删除了一些使代码更容易理解的可视标记.拥有更多语义缩进系统有助于缓解这一缺陷.

  • @morbidCode是的,但这取决于你的编辑.DrRacket具有自动缩进功能.Emacs有各种主要的模式来编辑不同类型的lisp代码.其他编辑器具有类似的功能. (2认同)

Rai*_*wig 7

Lisp有一些缩进代码的规则.使用最紧凑的版本.列表中元素的对齐很重要.还应考虑水平空间与可读性的最佳使用.

  • 宏和特殊表单可以有自定义缩进规则.见下文.

  • 函数有一些基于可用水平空间的缩进变体

例:

(append a b c)    ; all arguments fit on a line

(append a         ; arguments are aligned
        b
        c)

(append           ; saving horizontal space, elements are aligned
 a
 b
 c)
Run Code Online (Sandbox Code Playgroud)

像IF THEN ELSE这样的简单宏/运算符通常像函数一样对齐.

一个稍微复杂的情况是DEFINE:

(define (FUNCTION-NAME ARG0 ... ARGN) BODY-FORM-0 ... BODY-FORM-N)
Run Code Online (Sandbox Code Playgroud)

例子:

 (define (foo a b) (print a) (append a b))

 (define (foo a b)
   (print a)
   (append a b))

 (define (foo a
              b)
   (print a)
   (append a b))

 (define (foo
          a
          b)
   (print a)
   (append a b))
Run Code Online (Sandbox Code Playgroud)

典型的Lisp漂亮打印机将根据可用的水平空间选择缩进变体.