Lisp代码格式

Lisp代码格式,lisp,clojure,code-formatting,Lisp,Clojure,Code Formatting,一位花时间评论Clojure/LISP语法的人指出,我没有用标准的LISP方式编写示例代码。因此,他非常友好地重写了代码片段,这是一个很大的帮助。但这在我脑海中提出了另一个问题。为什么会这样: (if (= a something) (if (= b otherthing) (foo))) 以下哪种格式是标准LISP格式 (if (= a something) (if (= b otherthing) (foo) ) ) 这是我的C++开发背景下我会天真地格式化这

一位花时间评论Clojure/LISP语法的人指出,我没有用标准的LISP方式编写示例代码。因此,他非常友好地重写了代码片段,这是一个很大的帮助。但这在我脑海中提出了另一个问题。为什么会这样:

(if (= a something)
  (if (= b otherthing)
    (foo)))
以下哪种格式是标准LISP格式

(if (= a something)
  (if (= b otherthing)
    (foo)
  )
)

<>这是我的C++开发背景下我会天真地格式化这个代码的方式。我想知道后一种格式是否有什么好处,或者它只是一种根深蒂固的标准(比如QWERTY键盘)。我不想争论——我只是很难理解为什么第一种形式更可取。第二种形式帮助我更容易地查看代码结构

当你有10个括号要关闭时,它会变得非常笨重

当我使用Lisp编程时,我在同一行的左括号和右括号之间留了一个空格,以简化计数,如下所示:

(if (= a something) (if (= b otherthing) (foo) )) (如果某物) (如果(=b其他事物) (foo))
我想现在已经不需要了,因为编辑器更有用。

Lisp代码缩进的方式有点像Python中的重要空格,只是它当然是可选的。基本的经验法则是,如果列表中的项目不在同一行上,则将它们垂直放置在另一个项目的下方

(a (b (c (d e)
         (f g))
      (h i j))
   (k l m n))
甚至不用看括号,你就可以看到
(de)
(fg)
c
的参数,
(c(de)(fg))
(hij)
b
的参数,
(b(c(de)(fg))(hij))
(klm n)
a
的参数

对于您的示例,它的格式应该更正确,如下所示:

(if (= a something)
    (if (= b otherthing)
        (foo)))

    ^   ^
  notice how they line up

现在缩进的级别变得有意义了,您不再需要依靠平衡括号来获取信息,而且由于将它们与结束语句放在同一行更加紧凑,lispers就是这么做的。诚然,Lisp代码不需要以这种方式格式化,但这是人们使用并可以依赖的一种相当标准的约定。

额外行上的右括号实际上无助于查看代码的结构,因为您可以从缩进级别获得相同的信息。然而,第二种形式占用了几乎两倍的行数,迫使您在阅读代码时更频繁地滚动

如果您需要更仔细地检查嵌套的括号,那么突出显示匹配括号的编辑器将帮助您。当匹配的括号不太远时,这也会更容易


如果表达式太长、太复杂,不容易阅读,这也可能是一种迹象,表明您应该将部分功能提取到一个单独的函数中。

简单的答案是,您的方式与Lisp漂亮的打印机的方式不同。对于代码来说,拥有一种真正的格式总是一件好事,而pprint宏会将这种格式内置到语言中


当然,因为存在pprint宏,所以严格来说您不需要遵循标准代码格式,因为人们可以通过pprint运行您的代码,并获得他们习惯的代码。但是,由于其他所有人都使用pprint,或者手动近似使用pprint,如果不使用相同的方法,您将很难读取代码,并且您没有一个简单的宏可以将其代码转换为首选格式。

您可以使用包srafctor:重新格式化Lisp代码

一些演示:

  • 在Clojure。我在一个有10k行的Clojure文件上进行了测试,格式化所有文件大约需要10秒钟(在缩进上花费了大量时间,而不是重新排列代码)
  • 在Emacs Lisp中
可用命令:

  • srefactor lisp格式缓冲区
    :格式化整个缓冲区
  • srefactor lisp format defun
    :当前defun光标所在的格式
  • srefactor lisp format sexp
    :设置当前sexp光标所在的格式
  • srefactor lisp one line
    :将当前同一级别的sexp转换为一行;使用prefix参数,递归地将所有内部sexps转换为一行
格式化命令也可用于公共Lisp和Scheme


如果有任何问题,请提交一份问题报告,我很乐意解决。

关于空格和Python的好观点。您可以将括号视为编辑器确定缩进的助手,否则将忽略它们。顺便说一句,当你习惯了Lisp,你就再也看不到它们了。我同意,就Lisp作为一个语言家族而言,你的形式缩进方式比OP的方式更为传统,在美学上更为可取,但OP的方式实际上是(在我看来有点不幸的是)Clojure惯例。有趣的是,我刚刚检查了Emacs,它的缩进与带有IF的OP类似,但使用随机名称时,它的缩进与上面的一样。也许IF有一些特殊之处,但不一致性令人恼火。IF的不同缩进是因为“foo”是条件成立时发生的情况,因此不是IF.FWIW的参数。我见过Java编码标准要求将闭合卷曲放在同一行上,我认为这是一个有益的惯例;我得试试看。它可以在频繁编辑的情况下真正帮助代码,因为你可以看到LiseSPLIT应该去哪里。真的是可怕的建议。有什么可怕的吗?在LISP 1.5程序员手册中有一个例子,从1962:代替(如果(某物)(如果(= B其他事物)(FoO))),你可能需要考虑(当(和某物)(=b其他事物)(foo))