Подтвердить что ты не робот

Как я могу определить тонкие ошибки синтаксиса 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))

Я автоматически отступаю и использую подсветку круглых скобок, чтобы избежать подсчета круглых скобок. По большей части он работает как ветер, но иногда я обнаруживаю свои синтаксические ошибки только путем отладки. Что я могу сделать?

4b9b3361

Ответ 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 и тому подобное. Это позволяет вам перемещаться.