Как я могу определить тонкие ошибки синтаксиса Lisp?
Я новичок, играющий с 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))
Я автоматически отступаю и использую подсветку круглых скобок, чтобы избежать подсчета круглых скобок. По большей части он работает как ветер, но иногда я обнаруживаю свои синтаксические ошибки только путем отладки. Что я могу сделать?
Ответы
Ответ 1
Выделите sexps
Прежде всего, включите подсветку встроенного парного матча (show-paren-mode
), если это еще не сделано. Это всегда дает вам представление о том, какой уровень отступов вы находитесь.
Есть еще несколько сложных пакетов. Например, см. ответ TJ на mic-paren
. Или, хотя я не счел это подходящим для меня, но есть режим выделения в скобках Шумахера, который выделяет каждый блок выражений разными цветами. Даже У Эдварда О'Коннора есть режим, который выделяет текущий sexp.
Paredit
Используйте paredit-mode
, который поможет вам писать sexps. Вы можете легко перемещаться между sexps и переструктурировать код. Кроме того, он гарантирует, что скобки всегда сбалансированы. Когда я впервые попробовал это, мне было очень неприятно, как Paredit ограничивает способ кодирования, но с тех пор я привык работать с ним, я намного более продуктивен и никогда не путаюсь с открытием и закрытием круглых скобок.
Авто отступы
Используйте Emacs Starter Kit, который, по умолчанию, позволяет использовать много полезных помощников для кодирования в Elisp, как повторный отступ в новой строке.
ElDoc
emacs-lisp-mode
имеет несколько полезных расширений. Например, я всегда использую eldoc-mode
, который отображает в настоящее время список аргументов функции ввода или переменную docstring в области эха. Это также помогает вам легко узнать, находитесь ли вы в неправильном блоке.
Edebug
Я почти забыл упомянуть Edebug, который, как последний шанс, всегда помогает вам разобраться, что происходит в ваш код.
Ответ 2
Вот три конкретные вещи, которые вы можете сделать, чтобы помочь в определении проблем синтаксиса 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. Если скобки не совпадают, цвет подсветки отличается - и ярче. Когда соответствующая скобка находится вне экрана, она показывает вам, как выглядит этот конец в минибуфере (и сообщает, сколько строк у него есть).
Компиляция
Для немного более сложного метода вы можете запустить компилятор Elisp с помощью M-x compile-defun
. Например, когда я составил этот простой пример:
(defun mytestfun ()
(let ((cur (current-buffer)))
)
(set-buffer cur))
Я получил буфер с именем *Compile-Log*
, который сказал:
Предупреждение: ссылка на свободную переменную `Дворняжка '
Что подсказывало мне, что я использовал cur
вне инструкции let
, которая его определяла.
Изменить уровень отступов
Если вы хотите, чтобы отступ был более заметным, вы можете настроить переменную listp-body-indent
:
(setq lisp-body-indent 4) ;# default is 2
Вы также можете настроить, как различные конструкты отступают, но я не советую, потому что это будет нестандартным и может привести к путанице при просмотре кода Lisp.
Ответ 3
Один простой способ сделать это - переместить курсор в начало каждого из ваших условий cond
и посмотреть, где падает закрытый палец.
Ответ 4
"Лучшим" способом записи lisp является комбинация emacs + slime
Он обеспечивает выделение круглых скобок, tabcompletion, вы можете перейти прямо к документу hyperspec lisp, он предоставляет переменные для функций (см. Ниже) и многое другое.
(defun foo (bar) ...)
Когда вы начнете вводить текст (foo это покажет вам, что foo хочет один аргумент с именем bar.Таким образом, вы можете легко "угадать", какие аргументы выполняет функция. Это особенно удобно для функций, которые не соответствуют lisp соглашениям.
Ответ 5
Мне трудно видеть такие ошибок, когда разница в отступом является только одно пространство. Есть ли со временем это станет проще?
Не для меня, по крайней мере.. Это отчасти зависит от конкретного шрифта xterm, который вы используете, но я считаю, что мне нужен четырехмерный отступ для эффективной работы (да, я использую этот старый оригинальный шрифт xterm — SunOS 4), и даже два проблемных.
Я также использую подсветку круглых скобок, а клавиша "%
" - в vi
.
К сожалению, это не очень полезный ответ.
Ответ 6
Я вижу два подхода, которые вы можете предпринять, что может помочь в обоих ваших примерах.
Первый способ состоит в том, чтобы написать код, сначала рисуя большую картинку, а затем запишите детали позже. Для вашего первого примера вы могли бы начать с написания этого первого
(cond
(--
--)
(--
--)
(t
--))
тогда вы можете начать заполнять детали. --
похожи на наполнители TODO.
Второй пример. вы можете начать с
(defun test ()
--)
то заполните более как это:
(defun test ()
(unless (--)
--))
затем заполните еще.
Другой способ - настроить радужные разделители так, чтобы он выделял даже уровни parens в одном цвете, а нечетные уровни parens в другом цвете. Это поможет с формой cond. Это не поможет вашему второму примеру, но если он был (xyzzy)
вместо xyzzy
, он имел бы.
Ответ 7
Чтобы добавить ко всем другим ценным указателям, я бы сказал "использовать командные команды на основе структуры", backward-sexp
, forward-sexp
и тому подобное. Это позволяет вам перемещаться.