Почему сообщество Lisp предпочитает накапливать все скобки в конце функции?


26

Почему сообщество Lisp предпочитает накапливать все скобки в конце функции:

(defn defer-expensive [cheap expensive]
  (if-let [good-enough (force cheap)]
    good-enough
    (force expensive)))

Почему бы не использовать соглашение, такое как C или Java?
Ладно, Лисп намного старше этих языков, но я говорю о современных Лисперах.

(defn defer-expensive [cheap expensive]
  (if-let [good-enough (force cheap)]
    good-enough
    (force expensive)
  )
)

Примечание: фрагмент кода взят из книги «Радость Clojure».


13
Мне говорят, что во времена перфокарт было обычной практикой заканчивать программу на Лиспе с дополнительной картой с 80 правильными скобками, просто чтобы убедиться, что в программе достаточно совпадений со всеми открытыми скобками ,
Алджер

2
Вау, мне нравится. Я всегда ненавидел скобки, но твой второй пример выглядит мне приятным. Не так хорошо, как Python, но улучшение.
Эрик Уилсон

5
потому что они могут?
Muad'Dib

Было бы хорошо, если бы диалект Clojure продвигал отступы как структуру кода. Это полностью исключило бы скобки (например, F # и т. Д.). Конечно, скобки по-прежнему будут законными, если это необходимо.
intrepidis

Ответы:


28

Одна из причин, по которой языки на основе Algol поощряют использование фигурных скобок в своей собственной строке, состоит в том, чтобы поощрять добавление большего количества строк между разделительными скобками без необходимости перемещать фигурные скобки. То есть, если начать с

if (pred)
{
  printf("yes");
}

легко прийти и добавить еще одно утверждение в фигурные скобки:

if (pred)
{
  printf("yes");
  ++yes_votes;
}

Если бы оригинальная форма была

if (pred)
{ printf("yes"); }

тогда нам пришлось бы «сдвинуть» две скобки, но мой пример больше касается последних. Здесь скобки разграничивают то, что должно быть последовательностью утверждений , в основном вызываемых для побочного эффекта.

И наоборот, Лиспу не хватает заявлений; каждая форма - это выражение , дающее какое-то значение, даже если в некоторых редких случаях (если подумать о Common Lisp) это значение намеренно выбирается как «нет значений» через пустую (values)форму. Менее часто можно найти последовательности выражений , в отличие от вложенных выражений . Желание «открыть последовательность шагов до конечного разделителя» возникает не так часто, потому что, поскольку операторы уходят и возвращаемые значения становятся все более распространенной валютой, реже игнорируется возвращаемое значение выражения и, следовательно, больше редко можно оценить последовательность выражений только для побочного эффекта.

В Common Lisp prognформа является исключением (как и его братья и сестры):

(progn
  (exp-ignored-return-1)
  (exp-ignored-return-2)
  (exp-taken-return))

Здесь prognоценивает три выражения по порядку, но отбрасывает возвращаемые значения первых двух. Вы могли бы представить запись последней закрывающей скобки в отдельной строке, но еще раз обратите внимание, что, поскольку последняя форма здесь особенная ( хотя и не в смысле того , что Common Lisp является особенной ), с особой обработкой, более вероятно, что вы добавите новую выражения в середине последовательности, а не просто «добавление еще одного в конец», так как на вызывающих будут воздействовать не только какие-либо новые побочные эффекты, но скорее вероятное изменение возвращаемого значения.

Делая грубое упрощение, скобки в большинстве частей программы на Лиспе разграничивают аргументы, передаваемые функциям - так же, как в C-подобных языках, - а не разграничивают блоки операторов. По тем же причинам мы стремимся к тому, чтобы скобки, ограничивающие вызов функции в C, были близки к аргументам, так же как и в Lisp, и с меньшей мотивацией отклоняться от этой закрытой группировки.

Закрытие скобок имеет гораздо меньшее значение, чем отступ в форме, в которой они открываются. Со временем каждый учится игнорировать круглые скобки и писать и читать по форме - так же, как это делают программисты на Python. Однако не позволяйте этой аналогии привести вас к мысли, что полное удаление скобок будет иметь смысл. Нет, для этого лучше всего спорить comp.lang.lisp.


2
я думаю, что добавление в конце не так уж редко, например (let ((var1 expr1) more-bindings-to-be-added) ...), или(list element1 more-elements-to-be-added)
Алексей

14

Потому что это не помогает. Мы используем отступ, чтобы показать структуру кода. Если мы хотим разделить блоки кода, мы используем действительно пустые строки.

Поскольку синтаксис Lisp настолько непротиворечив, круглые скобки - это полное руководство для отступов как для программиста, так и для редактора.

(Для меня вопрос скорее в том, почему программисты на C и Java любят разбрасывать скобки.)

Просто для демонстрации, предполагая, что эти операторы были доступны на C-подобном языке:

Foo defer_expensive (Thunk cheap, Thunk expensive) {
    if (Foo good_enough = force (cheap)) {
        return good_enough; }
    else {
        return force (expensive); }}

Две закрывающие скобки закрывают два уровня вложенности. Синтаксис, очевидно, правильный. По аналогии с синтаксисом Python фигурные скобки - это просто явные токены INDENT и DEDENT.

Конечно, это не может быть ТМ "единого настоящего стиля" , но я считаю, что это просто историческая случайность и привычка.


2
Кроме того , почти все лисповские редактора отмечают соответствующую скобку при закрытии (некоторые из них также правильно , )чтобы ]или наоборот), так что вы знаете , что вы закрыли нужное количество , даже если вы не проверять уровень отступа.
конфигуратор

6
WRT «вопрос», потому что «разбрасывание» закрывающих токенов в стиле второго примера позволяет легко выстроить их в линию глазами и посмотреть, что закрывает, даже если вы просто в текстовом редакторе без автоматического сопоставления / Подсветка
Мейсон Уилер

1
@ Мейсон Уилер Да, именно так.
Хирон

3
TBH, это говорит мне о том, что парены в LISP в основном избыточны, с возможностью (как и в случае с C ++) отступ может ввести в заблуждение - если отступ говорит вам о том, что вам нужно знать, он должен сказать и компилятору тоже самое, как в Хаскеле и Питоне. Кстати, наличие фигурных скобок на том же уровне отступа, что и / / switch / while / в C ++, помогает предотвратить случаи, когда отступы вводят в заблуждение. Визуальное сканирование LHS показывает, что каждая открытая скобка сопоставлена ​​с закрывающей скобкой. и этот отступ соответствует этим скобкам.
Steve314

1
@ Steve314: Нет, отступы излишни. Отступы могут вводить в заблуждение и в Python, и в Haskell (подумайте об одноразовых отступах или таблицах), просто парсер также вводит в заблуждение. Я думаю, что круглые скобки - это инструмент для автора и анализатора, а отступы - для автора и читателя (человека). Другими словами, отступ - это комментарий, скобки - это синтаксис.
Сванте

11

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


Какая честь получить от вас ответ :) Спасибо.
Хирон

Какой редактор движется по s-выражениям? Более конкретно, как мне vimэто сделать?
hasen

2
@HasenJ Вам может понадобиться vimacs.vim плагин . : P
Марк С

5

Lispers, вы знаете, ненавистно bletcherous , как это может быть, там есть некоторая JE п саис Quoi ко второму примеру:

(defn defer-expensive [cheap expensive]
  (if-let [good-enough (force cheap)]
    good-enough
    (force expensive)
  )
)

Сначала я не мог указать, так сказать, источник очарования, а потом понял, что в нем просто отсутствует триггер:

(defn defer-expensive [cheap expensive]      
  (if-let [good-enough (force cheap)]
    good-enough   ;   )
    (force expensive) 
  )
)

Вуаля!

Я собираюсь попрактиковаться в гангстерском захвате Лиспа!


2
Это один из самых смешных и недооцененных ответов, которые я видел на этом сайте. Я снимаю шляпу
byxor

0

В моем случае я нахожу строки, посвященные разделителям, пустой тратой экранного пространства, а когда вы пишете код на языке C, появляется также стиль

if (pred) {
   printf("yes");
   ++yes_votes;
}

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

Вы достигнете того же результата, сложив скобки в конце. В Си выглядит странно, потому что операторы заканчиваются точкой с запятой, а пара скобок не открывается так

{if (pred)
    printf("yes");
}

это как эта закрывающая скобка в конце, выглядит неуместно. открывается так

if (pred) {
    printf("yes");
}

дает вам четкое представление о блоке и его границах с помощью '{' & '}'

А с помощью редактора, который сопоставляет круглые скобки, выделяя их, как это делает vim, вы можете перейти к концу, где упакованы все круглые скобки, легко перемещаться по ним, сопоставлять все открывающие скобки и видеть вложенную форму lisp.

(defn defer-expensive [cheap expensive]
    _(if-let [good-enough (force cheap)]
    good-enough
    (force expensive)_))

Вы можете поместить курсор в закрывающую область в середине и выделить начальную часть if-let.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.