Т.Л., д-р: она делает вложенность выражений ясного
Одной из определяющих особенностей Лиспов, в том числе схемы, является то, что код, который вы пишете, по существу, 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 на самом деле не делает этого ясно.
Отступ в схеме может показаться немного фанкивым, но как только вы привыкнете к нему, он делает код намного легче увидеть, не жонглируя круглыми скобками в голове. Однородность синтаксиса - это и благословение, и проклятие: он делает тривиальные макросы написания, но он удаляет некоторые визуальные маркеры, которые делают код более понятным. Наличие более семантической системы отступов помогает смягчить этот недостаток.
Вызовы функции имеют свои аргументы, совпадающие с первым. Это дает понять, чьими аргументами они являются, поэтому вам не нужно начинать считать парсеры, чтобы выяснить, что 'MAX-COUNT' является третьим аргументом для FACT-ITER (то же самое относится к' IF', хотя это курс не является функцией). Он также отличает списки аргументов от макросов. – jkiiski
@jkiiski мы добавили еще 4 пробела в «продукт», чтобы выровнять его с (если? Это правильно? Почему только добавить два пробела в «(если»? Почему 2? – morbidCode
@morbidCode: определенные «специальные» формы, такие как определяющие и связывающие формы, имеют свои собственные правила, которые (я думаю: я верю редактору знать), что «тело» формы получает отступы двумя символами. Это включает в себя такие вещи, как 'define' в Scheme,' defun' в CL и 'let' или' lambda'. Это, как правило, формы, в которых понимается понятие «тело». (Примечание. Я использую «специальный» здесь в нетехническом смысле). – tfb