我是一个玩弄Lisp的新手(实际上是Emacs Lisp)。这很有趣,除非我似乎一次又一次地遇到相同的语法错误。
例如,这是我多次遇到过的事情。我有一些cond
形式,比如
(cond
((foo bar)
(qux quux))
((or corge
(grault warg))
(fred)
(t
xyzzy)))
并且永远不会执行返回xyzzy
的default子句,因为它实际上嵌套在前一个子句中:
(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))
我习惯性地自动缩进并使用括号突出显示以避免计算括号。在大多数情况下,它像微风一样,但偶尔,我只通过调试发现我的语法错误。我该怎么办?
答案 0 :(得分:28)
首先,如果还没有这样做,请打开内置的paren匹配突出显示(show-paren-mode
)。它总能让您了解缩进级别。
还有一些更复杂的包。例如,请参阅TJ's answer on mic-paren
。或者虽然我没有发现它适合我,但有Schumacher's highlight-parentheses mode突出显示每个具有不同颜色的表达块。甚至Edward O'Connor has a mode突出了当前的性别。
使用paredit-mode
帮助您编写色情内容。您可以轻松地在sexps之间导航并重新构造代码。此外,它确保括号始终平衡。当我第一次尝试时,对我来说非常讨厌Paredit如何限制编码方式,但从那时起我就习惯了它,我的工作效率更高,永远不会对开括号和右括号感到困惑。 / p>
使用Emacs Starter Kit,默认情况下,它允许很多有用的帮助程序在Elisp中编码,就像在换行符上重新缩进一样。
emacs-lisp-mode
有几个有用的扩展。例如,我总是使用eldoc-mode
在echo区域中显示当前键入函数的参数列表或变量的docstring。它还可以帮助您轻松识别您是否在错误的区域。
我几乎忘记提及Edebug - 作为最后一次机会 - 总能帮助您弄清楚代码中发生了什么。
答案 1 :(得分: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)
当该点位于性别的开始/结束时,这会导致性别(匹配的括号)突出显示。如果括号不匹配,则突出显示颜色不同 - 并且更亮。当匹配的括号在屏幕外时,它会显示迷你缓冲区中的那个结尾(并告诉你它有多少行)。
对于稍微复杂一点的方法,可以使用M-x compile-defun
运行Elisp编译器。例如,当我编译这个简单的例子时:
(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代码时可能会引起混淆。
答案 2 :(得分:2)
要做的一件简单的事情就是将光标移动到每个cond
条件的开头,并查看关闭窗口的位置。
答案 3 :(得分:2)
编写lisp的“最佳”方式是emacs + slime组合 它提供括号突出显示,tabcompletion,你可以直接跳转到lisp hyperspec文档,它提供函数的变量名(见下文)等等。
(defun foo(bar) ...)
当你开始输入时(foo,它会告诉你foo想要一个名为bar的参数。这样你就可以轻松地“猜测”函数所采用的参数。这对于不遵循Lisp约定的函数尤其方便。 / p>
答案 4 :(得分:1)
我很难看到这样的 错误时的差异 缩进只是一个空间。是否 随着时间的推移这会变得更容易吗?
不适合我,至少..它在某种程度上取决于您使用的特定xterm字体,但我发现我需要一个四空缩进才能有效工作(是的,我使用那个旧的原始xterm字体 - 责备SunOS 4),甚至两个都是有问题的。
我也使用括号突出显示,%
中的“vi
”键很多。
不幸的是,这不是一个非常有用的答案。
答案 5 :(得分:1)
我看到你可以采取的两种方法可以帮助你完成这两个例子。
第一种方法是首先绘制一张大图来编写代码,然后再填写详细信息。对于您的第一个示例,您可以通过编写第一个
开始(cond
(--
--)
(--
--)
(t
--))
然后你就可以开始填写细节了。 --
就像TODO填充物。
你的第二个例子。你可以从
开始(defun test ()
--)
然后填写更多这样的内容:
(defun test ()
(unless (--)
--))
然后填写更多。
另一种方法是自定义彩虹分隔符,以便突出显示一种颜色的均匀水平,以及另一种颜色的奇数级别的parens。这将有助于cond形式。它对您的第二个示例没有帮助,但如果它是(xyzzy)
而不是xyzzy
,则会有。{/ p>
答案 6 :(得分:0)
要添加其他人的宝贵指示,我会说“使用基于结构的移动命令”,backward-sexp
,forward-sexp
等。这允许你导航。