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

Lisp Круглые скобки

Почему Lispers форматирует свой код, как показано в примере 1, а не как показано в примере 2? Для меня (и, я думаю, для большинства других, исходящих из разных программных фонов, чем Lisp), форматирование, показанное в примере 2, было бы легче читать. Есть ли какая-то особая причина, по которой Lispерс предпочитает стиль образца 1?

Пример 1

(defun factorial (n)
  (if (<= n 1)
    1
    (* n (factorial (- n 1)))))

Образец 2

(defun factorial (n)
  (if (<= n 1)
    1
    (* n (factorial (- n 1)))
  )
)
4b9b3361

Ответ 1

LISP среды IDE имеют тенденцию автоматически балансировать круглые скобки и управлять отступами на основе уровня вложенности. Образец 2 не приносит никаких преимуществ в этих ситуациях.

В наследии C/FORTRAN/Pascal вы, как правило, подчеркиваете последовательность из-за гнездования (деревья анализа кода более мелкие и более широкие). Конец области действия является более значимым событием в вашем коде: поэтому акцент был и остается в какой-то мере более важным.

Ответ 2

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

Основная идея заключается в том, что круглые скобки напрямую связаны с их содержимым.

(a)

а не

(a
)

Далее следует следующее:

(defun foo (bar)
  (foo (bar (baz
              ...
             )
         ...
        )
    ...
   )
 )

против.

(defun foo (bar)
  (foo (bar (baz ...) ...) ...))

Одна из основных идей при редактировании текста Lisp заключается в том, что вы можете выбрать список по двойному щелчку на круглых скобках (или с помощью команды ключа, когда курсор находится внутри выражения или в круглых скобках), Затем вы можете вырезать/скопировать выражение и вставить его в другое место в какой-либо другой функции. Следующий шаг - выбрать другую функцию и переустановите функцию. Готово. Нет необходимости удалять или вводить новые строки для закрытия круглых скобок. Просто вставьте и снова отступайте. Он просто вписывается. В противном случае вы либо потратили бы время на форматирование кода, либо вам нужно было бы переформатировать код с помощью некоторого инструмента редактора (так что закрывающиеся круглые скобки находятся в их собственных строках. В большинстве случаев это создавало бы дополнительную работу и препятствует перемещению кода вокруг.

Есть один случай, когда опытные Lispеры когда-нибудь пишут закрывающие круглые скобки в своей собственной строке:

(defvar *persons*
   (list (make-person "fred")
         (make-person "jane")
         (make-person "susan")
         ))

Здесь указывается, что новые люди могут быть добавлены. Поместите курсор непосредственно перед вторыми закрывающимися круглыми скобками в последней строке, нажмите c-o (открытая строка), добавьте предложение и отступом скобки, которые они выровнены снова. Это сохраняет "проблему", чтобы найти правильные круглые скобки, а затем нажмите "возврат", когда все круглые скобки закрыты в одной строке.

Ответ 3

Потому что нет необходимости выстраивать закрывающие парсеры. Он ничего не добавляет семантически. Чтобы узнать, как много закрыть, большинство Lispers используют хороший редактор, такой как Emacs, который соответствует parens для закрытия parens и, следовательно, делает задачу тривиальной.

В Python нет закрывающих парсеров или end ключевых слов, а Pythonistas живут нормально.

Ответ 4

Через некоторое время с Lisp вы больше не заметите круглые скобки, так что ваш пример два встречается с кучей ненужных пробелов в конце его. Более конкретно, большинство lispers используют редактор, который знает круглые скобки и заботится о том, чтобы закрыть формы правильно, так что вам, как разработчику, не нужно сопоставлять открывающие и закрывающие круглые скобки, как вы это делаете, например. С.

В зависимости от того, должна ли быть последняя форма

(* n (factorial (- n 1)))

или

(* n
   (factorial (- n 1)))

что в основном сводится к личным предпочтениям и тому, как много вещей происходит в коде (в этом случае я предпочел бы первое, потому что в коде мало чего происходит).

Ответ 5

Кроме того, что большинство Lisp IDE используют сопоставление парнов, объем пространства, который вы использовали бы для написания любой разумной программы, был бы смешным! Из всей прокрутки вы получите запястный туннель. Программа с 1000 линиями будет близка к миллиону строк кода, если вы поместите все закрывающие круглые скобки на свою линию. Это может выглядеть красивее и легче читать в небольшой программе, но эта идея не будет хорошо масштабироваться.

Ответ 6

Почему программисты C пишут такие вещи, как:

((a && b) || (c && d))

вместо

((a && b) || (c && d
             )
)

Не было бы легче читать 2?:)

Серьезно, однако, сгруппированный стиль закрытия хорошо работает и для других языков.

#include <stdlib.h>
#include <stdio.h>

int main(void)
{ int i, j, k;
  for (i = 0; i < 1000; i++)
  { for (j = 0; j < 1000; j++)
    { for (k = 0; k < 1000; k++)
      { if (i * i + j * j == k * k)
        { printf("%d, %d, %d\n", i, j, k); } } } }
  return 0; }

Через некоторое время вы не увидите "фигурные скобки", а только структуру, заданную отступом. if/else выглядит так:

if (cond)
{ stmt;
  stmt; }
else
{ stmt; }

переключатель:

switch (expr)
{ case '3':
    stmt;
    break;
  case '4':
  { stmt;
    break; } }

Структура:

struct foo
{ int x;
  struct bar
  { int y; };
  union u
  { int a, b;
    char c; }; };

Ответ 7

Я столкнулся с той же дилеммой в PHP, используя фреймворк CakePHP и множество вложенных структур array(), иногда более 5 слоев. Через некоторое время я понял, что, будучи ОКР о закрывающейся скобке, ничего не делаю, кроме как тратить свое драгоценное время разработки и отвлекать меня от конечной цели. Отступ должен был быть наиболее полезным аспектом форматирования.

Ответ 8

Сохранение всего пространства кажется основной причиной. Если это почти так же читаемо (например, когда вы привыкли к синтаксису), зачем использовать дополнительные 3 строки.

Ответ 9

Только первая и очевидная причина: 4 LOC в первом случае против 7 LOC во втором.

Любой текстовый редактор, который знает синтаксис LISP, выделит вам совпадающие/несогласованные круглые скобки, так что это не проблема.

Ответ 10

Потому что Lisp программисты смотрят на отступы и "фигуру", а не на скобки.

Ответ 11

Вы можете рассмотреть варианты Lisp, которые обойдутся без(). В Genyris это выглядит так:

def factorial (n)
  if (< n 2) 1
    * n
      factorial (- n 1)

Ответ 12

Похоже, что это не вопрос, а просто суждение о Lisp, но ответ совершенно очевиден (но, видимо, не для не-Lispers!): круглые скобки в Lisp никоим образом не эквивалентны скобки в C-подобных языках - скорее, они точно эквивалентны круглым скобкам на языках C-типа. Lispерс обычно не пишет

(foo bar baz
)

по той же причине, что программисты C обычно не пишут

foo(bar, baz
   );

Причина

(if foo
    (bar)
    (baz))

отличается от

if (foo) {
   bar();
} else {
   baz();
}

имеет больше общего с if, чем круглые скобки!

Ответ 13

У меня есть объяснение, связанное с тем, что программисты C имеют проблемы с тем, что Lispers помещает все оставшиеся закрывающие круглые скобки в конце. Мне кажется, что смысл в скобках может быть причиной. Это объяснение дублирует и расширяет некоторые другие ответы.

Для программиста на C (или другого языка, использующего {фигурные скобки}, такие как C), Lisp выглядит так: # | комментарии | # вместо /* comments */. И выглядит как Lisp круглые скобки() вместо C braces {}.

Но действительно, скобки Lisp означают очень близкую к той же вещи, что и в C (и аналогичных языках, используя круглые скобки). Рассмотрим

defun f () nil

Это определяет функцию f, которая абсолютно ничего не делает, nil. И я набираю эту строку точно так же, как в Lisp Listener, и она просто отвечает "F". Другими словами, он принимает это, без начала и конца круглых скобок вокруг всего.

То, что я думаю, происходит, когда вы вводите это в Listener, Listener передает то, что вы набрали для Eval. Передача его Eval может быть представлена ​​как:

Eval( Listener() )

И прослушиватель принимает мой ввод и возвращает то, что я набрал. И вы считаете, что "Listener()" заменено в выражении так, что оно становится

Eval (defun f () nil)

REPL (read, eval, print, loop) можно представить концептуально как рекурсия

/* REPL expressed in C */
Loop()
{
    Print ( Eval ( Listen () ) );
    Loop();
}

В этом примере понимается, что Listen(), Eval() и Print() определены в другом месте или встроены в язык.

Слушатель позволяет мне вводить

setf a 5

а также позволяет мне вводить

(setf a 5)

но жалуется, когда я печатаю

((setf a 5))

Если скобки были эквивалентны скобкам языка C, слушатель принял бы это. В моих компиляторах C\С++ я могу напечатать

void a_fun(void)
{{
}}

без жалобы. Я даже могу напечатать

void a_fun(void)
{{{
}}}

без жалобы от компилятора C/С++.

Но если я осмелюсь набрать

void a_fun((void))
{
}

мой компилятор C/С++ жалуется - так же, как жалобщик Lisp жалуется!

Для программиста C, пишущего вложенные вызовы функций, кажется более естественным писать

fun_d( fun_c( fun_b( fun_a())));

чем писать

fun_d( fun_c( fun_b( fun_a()
                   )
             )
      );

В C скобки разграничивают блок кода. И блок кода является частью определения функции. Lisp круглые скобки не разграничивают блоки. Они как-то похожи на круглые скобки. В C круглые скобки разграничивают список; возможно, список параметров, переданных функции.

В Lisp, кажется, открываются круглые скобки "(" означает, что мы собираемся начать новый список, на который будет ссылаться сторона CAR конструкции CONS. Затем мы перечислим элементы, которые должны содержаться или, возможно, указывать на сторона CAR из CONS, сторона CDR указывает на nil (для конца списка) или следующую CONS. Закрывающая скобка ")" означает прекратить список с нулем и вернуться назад, чтобы продолжить предыдущий уровень листинга, Таким образом, язык C не выполняет CONS. Таким образом, существует небольшая разница между скобками C и Lisp. Тем не менее, кажется очень близким, может быть, даже практически одним и тем же.

Некоторые пишут, что Lisp становится больше похожим на C, если вы перемещаете функцию вне круглых скобок. Например,

(setf a 5)

становится

setf(a 5)

Но на самом деле это то, что Eval требует, чтобы первым элементом в списке была функция, которую Eval должен вызывать. Это больше похоже на передачу функции C указателем на функцию. Итак, на самом деле это

eval(setf a 5)

И это больше похоже на C. Вы даже можете ввести его в слушателе так же, как без жалобы слушателя или eval. Вы просто говорите, что eval должен вызвать eval, который затем должен вызвать setf. Остальная часть списка - это параметры для setf.

Во всяком случае, это то, что я думаю, что вижу.

Это просто, что Eval должен знать, какой процессор вызывать.

И я думаю, что это объяснение дает понимание того, почему программисты C сначала думают, что Lispерс должен выровнять закрывающиеся круглые скобки в скобках, которые они закрывают. Программист C ошибочно полагает, что скобка Lisp соответствует скобкам C. Они этого не делают. Lisp круглые скобки соответствуют скобкам C.