14

我是一个使用 Lisp(实际上是 Emacs Lisp)的新手。这很有趣,除非我似乎一次又一次地遇到相同的语法错误。

例如,这是我多次遇到的情况。我有一些cond形式,比如

(cond
 ((foo bar)
  (qux quux))
 ((or corge
      (grault warg))
  (fred)
  (t
   xyzzy)))

并且返回的默认子句xyzzy永远不会执行,因为它实际上嵌套在前一个子句中:

(cond
 ((foo bar)
  (qux quux))
 ((or corge
      (grault warg))
  (fred))
 (t
  xyzzy))

当缩进的差异只有一个空格时,我很难看到这样的错误。随着时间的推移,这会变得更容易吗?

当(错误)缩进线和它应该缩进的线之间的距离很大时,我也会遇到问题。let例如,具有许多复杂绑定的unless表单,或具有长条件的表单:

(defun test ()
  (unless (foo bar
               (qux quux)
               (or corge
                   (grault warg)
                   (fred))))
  xyzzy)

事实证明xyzzy根本不在unless表单内:

(defun test ()
  (unless (foo bar
               (qux quux)
               (or corge
                   (grault warg)
                   (fred)))
    xyzzy))

我习惯性地自动缩进并使用括号突出显示来避免计算括号。在大多数情况下,它的工作就像轻而易举,但偶尔,我只是通过调试才发现我的语法错误。我能做些什么?

4

7 回答 7

28

突出性别

首先,show-paren-mode如果还没有打开内置的括号匹配突出显示 ( )。它总是让您了解您所处的缩进级别。

还有一些更复杂的软件包。例如,请参阅TJ 对 的回答mic-paren。或者虽然我觉得它不适合我,但是舒马赫的高亮括号模式可以用不同的颜色突出显示每个表情块。甚至爱德华·奥康纳(Edward O'Connor)也有突出当前性别的模式。

编辑

使用paredit-mode它可以帮助您编写 sexps。您可以轻松地在 sexps 之间导航并重新构建代码。此外,它确保括号始终平衡。当我第一次尝试它时,Paredit 如何限制编码方式对我来说非常烦人,但从那以后我习惯了使用它,我的工作效率提高了很多,并且永远不会对左括号和右括号感到困惑。

自动缩进

使用Emacs Starter Kit,默认情况下,它为 Elisp 中的编码提供了许多有用的帮助程序,例如在换行符上重新缩进。

埃尔多克

emacs-lisp-mode有几个有用的扩展。例如,我总是使用eldoc-mode在回显区域显示当前正在输入的函数的参数列表或变量的文档字符串。它还可以帮助您轻松识别您是否在错误的街区。

调试

我差点忘了提到Edebug,作为最后的机会,它总是可以帮助您弄清楚代码中发生了什么。

于 2009-05-22T15:08:49.343 回答
7

以下是您可以做的三件事来帮助您发现 Lisp 语法问题。随着时间的推移,它会成为第二天性。但在那之前:

括号匹配

括号匹配是检查分组的最简单方法。我最喜欢的包是mic-paren。我喜欢这个特殊的配置:

(setq paren-dont-touch-blink t)
(require 'mic-paren)
(paren-activate)
(setq paren-match-face 'highlight)
(setq paren-sexp-mode t)

这会导致当点位于 sexp 的开头/结尾时突出显示 sexp(匹配括号)。如果括号不匹配,则突出显示颜色不同 - 并且更亮。当匹配的括号不在屏幕上时,它会向您显示该端在 minibuffer 中的样子(并告诉您它有多少行)。

汇编

对于稍微复杂一点的方法,您可以使用M-x compile-defun. 例如,当我编译这个简单的例子时:

(defun mytestfun ()
  (let ((cur (current-buffer)))
    )
  (set-buffer cur))

我有一个名为的缓冲区*Compile-Log*,它说:

警告:对自由变量“cur”的引用

这使我了解到我在定义它cur的语句之外使用的事实。let

更改缩进级别

如果您希望缩进更突出,您可以自定义变量listp-body-indent

(setq lisp-body-indent 4) ;# default is 2

您还可以自定义各种结构的缩进方式,但我不建议这样做,因为它是非标准的,并且在查看大多数 Lisp 代码时可能会导致混淆。

于 2009-05-22T15:35:22.850 回答
2

一件简单的事情是将光标移动到每个cond条件的开头,然后查看结束括号的位置。

于 2009-05-22T15:02:19.933 回答
2

编写 lisp 的“最佳”方式是 emacs + slime 组合。它提供括号突出显示、制表符补全,您可以直接跳转到 lisp hyperspec 文档,它提供函数的变量名(见下文)等等。

(defun foo (bar) ...)

当你开始输入 (foo 它会告诉你 foo 想要一个名为 bar 的参数。这样你可以很容易地“猜测”一个函数需要什么参数。这对于不遵循 Lisp 约定的函数特别方便。

于 2009-05-22T15:08:37.520 回答
1

当缩进的差异只有一个空格时,我很难看到这样的错误。随着时间的推移,这会变得更容易吗?

至少不适合我.. 它确实部分取决于您使用的特定 xterm 字体,但我发现我需要一个四空格缩进才能有效工作(是的,我使用旧的原始 xterm 字体 - 归咎于 SunOS 4) ,甚至两个都是有问题的。

我也使用括号突出显示,并且“ %”键在vi.

不幸的是,这不是一个非常有用的答案。

于 2009-05-22T14:58:59.457 回答
1

我看到您可以采取两种方法来帮助您的两个示例。

第一种方式是先画大图写代码,后面再填写细节。对于你的第一个例子,你可以先写这个

(cond
 (--
  --)
 (--
  --)
 (t
  --))

然后您可以开始填写详细信息。' --s 就像 TODO 填充物。

你的第二个例子。你可以从

(defun test ()
  --)

然后填写更多这样的:

(defun test ()
  (unless (--)
    --))

然后填充更多。

另一种方法是自定义彩虹分隔符,以便它以一种颜色突出显示偶数级别的括号,并以另一种颜色突出显示奇数级别的括号。这将有助于 cond 形式。它对您的第二个示例没有帮助,但如果它是(xyzzy)而不是xyzzy,它将有。

于 2013-08-20T15:01:05.797 回答
0

为了补充其他人的宝贵意见,我会说“使用基于结构的移动命令” backward-sexpforward-sexp等等。这使您可以四处导航。

于 2010-04-30T10:33:11.790 回答