Чому спільнота 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)
  )
)

Примітка: фрагмент коду - з книги "Радість клоджура".


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

2
Нічого собі, мені це подобається. Я завжди ненавидів дужки, але ваш другий приклад мені здається приємним. Не так приємно, як Python, але покращення.
Ерік Вілсон

5
бо вони можуть?
Muad'Dib

Було б добре, якби діалект Clojure сприяв відступу до структури коду. Це взагалі скасує дужки (наприклад, F # тощо). Звичайно, дужки в дужках все-таки були б законними.
intrepidis

Відповіді:


28

Однією з причин, що на основі алгольських мов спонукають дужки на власній лінії, - це заохочення додавати більше рядків між розмежувальними дужками, не потребуючи переміщення дужок. Тобто, якщо хтось починає з

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

легко зійти і додати ще одне твердження в дужки:

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

Якби початкова форма була

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

тоді нам довелося б "перенести" дві дужки, але мій приклад більше стосується останньої. Тут дужки розмежують те, що має бути послідовністю висловлювань , в основному викликається побічним ефектом.

І навпаки, у Ліспа бракує тверджень; кожна форма є вираженням , приносячи деяку цінність - навіть якщо в деяких рідкісних випадках (маючи на увазі загального Ліса), це значення навмисно вибирається таким, що не має значення без порожньої (values)форми. Рідше можна знайти послідовності виразів , на відміну від вкладених виразів . Бажання "відкрити послідовність кроків до розмежувача, що закривається" виникає не так часто, тому що, коли заяви й відходять, а значення повернення стають більш звичною валютою, рідше ігнорувати повернене значення виразу, а отже, і більше рідко можна оцінити послідовність виразів лише для побічного ефекту.

У Common Lisp prognформа є винятком (як і її побратими):

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

Тут проводиться prognоцінка трьох виразів у порядку, але відкидає значення повернення перших двох. Ви можете собі уявити, як писати цю останню дужку в окремому рядку, але зауважте ще раз, що оскільки остання форма тут особлива (але не в загальному сенсі Lisp, щоб бути особливою ), з виразним поводженням, більш ймовірно, що ви додасте нові вирази в середині послідовності, а не просто "додавання ще одного до кінця", оскільки на абонентів тоді впливатимуть не просто будь-які нові побічні ефекти, а скоріше ймовірна зміна зворотного значення.

Здійснюючи грубе спрощення, дужки в більшості частин програми Lisp розмежують аргументи, передані функціям, як і в мовах, схожих на С, і не обмежують блоки операторів. З тих самих причин ми, як правило, утримуємо круглі дужки, що обмежують виклик функції в 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 люблять кидати свої брекети.)

Просто для того, щоб продемонструвати, припускаючи, що ці оператори були доступні на мові, подібній С:

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
@ Мейсон Уілер Так, саме так.
Chiron

3
TBH, що це говорить мені, що парен у LISP здебільшого є надлишковим, з можливістю (як і на C ++) відступ може ввести в оману - якщо відступ говорить вам, що вам потрібно знати, він повинен повідомити компілятору те саме, як у Haskell та Python. BTW - дужки на тому ж рівні відступу, що і якщо / перемикач / в той час, як у C ++ допомагає запобігти випадкам, коли відступ вводить в оману - візуальне сканування вниз за LHS говорить про те, що кожна відкрита дужка відповідає тісному дужці і цей відступ відповідає цим дужкам.
Стів314

1
@ Steve314: Ні, відступ є зайвим. Відступ може ввести в оману і в Python та Haskell (подумайте про разові відступи або таблиці), парсер також вводить в оману. Я думаю, що дужки - це інструмент для письменника та аналізатора, тоді як відступ - це інструмент для письменника та (людського) читача. Іншими словами, відступ - це коментар, дужки - це синтаксис.
Svante

11

Код тоді набагато компактніший. Рух у редакторі все-таки здійснюється за допомогою s-виразів, тому вам не потрібен простір для редагування. Код читається здебільшого за структурою та реченнями, а не за наступними роздільниками.


Яка честь отримати від вас відповідь :) Дякую
Хірон

Який редактор рухається за допомогою s-виразів? Більш конкретно, як мені vimце зробити?
hasen

2
@HasenJ Можливо, вам знадобиться vimacs.vim плагін . : P
Марк C

5

Слухачі, ви знаєте, ненависно блестячі, як це може бути, є певний je ne sais 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) 
  )
)

Вуаля!

Я зараз буду займатися моїм бандитським захватом Lisp!


2
Це одна із найсмішніших та найзаниженіших відповідей, яку я бачив на цьому сайті. Я накидаю шапку.
byxor

0

У моєму випадку я вважаю, що рядки, присвячені розмежувачам, марно витрачаються на екранний простір, і коли ви пишете код на C, також існує стиль

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

Чому люди ставлять вступну дужку в ту саму лінію "якщо", щоб заощадити простір, і тому, що здається зайвим мати свою лінію, коли "якщо" вже має свою лінію.

Ви досягаєте того ж результату, склавши дужки в кінці. На мові C це виглядатиме дивно, оскільки заяви закінчуються крапкою з комою та пара дужок не відкривається так

{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.