2016-03-18 2 views
1

Рассмотрите эту итеративную факториальную процедуру от SICP.Как форматировать код lisp?

(define (fact-iter product counter max-count) 
    (if (> counter max-count) 
     product 
     (fact-iter (* counter product) 
       (+ counter 1) 
       max-count))) 

Здесь мы видим:

  • Декларация факториала не имеет начальные пробелы. Я думаю, это нормально.
  • Для тела функции требуется две ведущие пробелы в каждой строке.
  • мы добавляем четыре пространства в первом предложении и в начало второго предложения инструкции if. Всего 6 пробелов.
  • Мы добавим еще 11 пробелов в последних двух строках, остальное второе предложение оператора if. Всего 17 пробелов.

Почему так? Интервал меня сбивает с толку. Почему это не похоже на java (добавьте четыре пробела во внутреннюю часть кода)? Как мне форматировать lisp-код?

+0

Вызовы функции имеют свои аргументы, совпадающие с первым. Это дает понять, чьими аргументами они являются, поэтому вам не нужно начинать считать парсеры, чтобы выяснить, что 'MAX-COUNT' является третьим аргументом для FACT-ITER (то же самое относится к' IF', хотя это курс не является функцией). Он также отличает списки аргументов от макросов. – jkiiski

+0

@jkiiski мы добавили еще 4 пробела в «продукт», чтобы выровнять его с (если? Это правильно? Почему только добавить два пробела в «(если»? Почему 2? – morbidCode

+0

@morbidCode: определенные «специальные» формы, такие как определяющие и связывающие формы, имеют свои собственные правила, которые (я думаю: я верю редактору знать), что «тело» формы получает отступы двумя символами. Это включает в себя такие вещи, как 'define' в Scheme,' defun' в CL и 'let' или' lambda'. Это, как правило, формы, в которых понимается понятие «тело». (Примечание. Я использую «специальный» здесь в нетехническом смысле). – tfb

ответ

8

Т.Л., д-р: она делает вложенность выражений ясного


Одной из определяющих особенностей Лиспов, в том числе схемы, является то, что код, который вы пишете, по существу, AST (абстрактное синтаксическое дерево). Такие языки, как Java, имеют большой синтаксис, поэтому они требуют, чтобы парсеры правильно отображали неоднозначные грамматики. Операторы Infix являются классическим примером этого. Рассмотрим следующий оператор в Java:

int x = 1 + y * 2; 

Текстовое представление кода, конечно, не означает какого-либо древовидную структуру, но в действительности, это утверждение имеет единственный канонический разобрана, что фактически дерево , Это будет выглядеть примерно так:

 = 
    /\ 
int x + 
    /\ 
    1 * 
     /\ 
     y 2 

Эквивалентный код Scheme, с другой стороны, делает все, что гнездящихся невероятно четко:

(define x (+ 1 (* y 2))) 

Обратите внимание, как явная группировка создает очень хорошо определенное дерево выражений. Нет необходимости в правилах приоритетов операторов, как на большинстве других языков. Эта простота является преднамеренным выбором дизайна, потому что, когда представление исходного кода так просто, очень легко писать макросы, которые его преобразуют. Lisps имеют тенденцию активно использовать макросы, потому что манипулирование АСТ относительно безболезненно по сравнению с другими языками программирования из-за того, насколько прост синтаксис.


С учетом всего этого, правила отступа могут стать более очевидными: Lisp код, как правило, с отступом таким образом, чтобы структура AST сразу видно. Рассмотрим альтернативный вариант вашего fact-iter пример функции, которая используется в «простой» отступа стиль:

(define (fact-iter product counter max-count) 
    (if (> counter max-count) 
    product 
    (fact-iter (* counter product) 
     (+ counter 1) 
     max-count))) 

В данном конкретном случае, отступы не катастрофично, но рекурсивный вызов fact-iter теперь намного более трудно визуально анализировать , Однородность синтаксиса Lisp/Scheme затрудняет немедленный прием факта, что fact-iter вызывается с тремя аргументами, потому что первый аргумент больше не совпадает с последними двумя.

Это может быть по крайней мере, до некоторой степени решаться просто положить все аргументы в отдельных строках:

(fact-iter 
(* counter product) 
(+ counter 1) 
max-count) 

Это работает, и на самом деле приемлемым Lisp стиль. Это часто представляет собой значительную трату вертикального пространства, и, тем не менее, это еще более затрудняет сборку AST, поскольку отступы значительно менее визуально поражают.


Для примера, в котором с помощью «простой» модели отступы губительно на схеме, рассмотрим два следующих эквивалентных выражений:

(string->number (if (string? x) x 
        (format "~a" x))) 

(string->number (if (string? x) x 
    (format "~a" x))) 

Первый пример поддерживает AST. Очень легко видеть, что вызов format является случаем «else» для формы if, потому что он находится под этим. Второй пример не поддерживает AST, и на первый взгляд неясно, если вызов format вложен внутри if или если это просто второй аргумент, переданный string->number. Вы можете видеть, что синтаксис Lisp на самом деле не делает этого ясно.

Отступ в схеме может показаться немного фанкивым, но как только вы привыкнете к нему, он делает код намного легче увидеть, не жонглируя круглыми скобками в голове. Однородность синтаксиса - это и благословение, и проклятие: он делает тривиальные макросы написания, но он удаляет некоторые визуальные маркеры, которые делают код более понятным. Наличие более семантической системы отступов помогает смягчить этот недостаток.

+1

Есть ли инструмент, который автоматически это сделает? – morbidCode

+2

@morbidCode Да, но это зависит от вашего редактора. DrRacket имеет функцию автоматического отступа. Emacs имеет различные основные режимы для редактирования различных типов lisp-кода. Другие редакторы имеют схожие функции. –

6

В Lisp есть несколько правил для отступов. Используйте самую компактную версию. Важное значение имеет выравнивание элементов в списке. Также следует учитывать оптимальное использование горизонтального пространства и читаемости.

  • макросы и специальные формы могут иметь индивидуальные правила отступов. Смотри ниже.

  • функция имеет несколько вариантов отступа на основе имеющихся горизонтального пространства

Пример:

(append a b c) ; all arguments fit on a line 

(append a   ; arguments are aligned 
     b 
     c) 

(append   ; saving horizontal space, elements are aligned 
a 
b 
c) 

простого макро/оператор, как если потом еще, как правило, выравниваются, как функция.

Несколько более сложная ситуация ОПРЕДЕЛИТЬ:

(define (FUNCTION-NAME ARG0 ... ARGN) BODY-FORM-0 ... BODY-FORM-N) 

Примеры:

(define (foo a b) (print a) (append a b)) 

(define (foo a b) 
    (print a) 
    (append a b)) 

(define (foo a 
       b) 
    (print a) 
    (append a b)) 

(define (foo 
      a 
      b) 
    (print a) 
    (append a b)) 

Типичная Lisp довольно принтер выбрать отступы варианты, основанные на доступных горизонтального пространства.

Смежные вопросы