LET
самостоятельно не реальный примитив в Язык Функционального программирования , так как он может замененный LAMBDA
. Как это:
(let ((a1 b1) (a2 b2) ... (an bn))
(some-code a1 a2 ... an))
подобно [1 122]
((lambda (a1 a2 ... an)
(some-code a1 a2 ... an))
b1 b2 ... bn)
, Но
(let* ((a1 b1) (a2 b2) ... (an bn))
(some-code a1 a2 ... an))
подобно [1 124]
((lambda (a1)
((lambda (a2)
...
((lambda (an)
(some-code a1 a2 ... an))
bn))
b2))
b1)
, можно вообразить, который является более простой вещью. LET
а не LET*
.
LET
делает код, понимающий легче. Каждый видит набор привязки, и можно считать каждую привязку индивидуально без потребности понять top-down/left-right поток 'эффектов' (повторные переплетения). Используя [1 110] сигналы программисту (тот, который читает код), что привязка весьма зависима, но существует некоторый нисходящий поток - который усложняет вещи.
язык Common LISP имеет правило, что значения для привязки в [1 111] вычисляются слева направо. Как значения для вызова функции оценены - слева направо. Так, LET
концептуально более простой оператор, и он должен использоваться по умолчанию.
Типы в [1 113] ? Используются довольно часто. Существуют некоторые примитивные формы описания типа, которые легко помнить. Пример:
(LOOP FOR i FIXNUM BELOW (TRUNCATE n 2) do (something i))
Выше объявляет, что переменная i
fixnum
.
Richard P. Gabriel опубликовал свою книгу по сравнительным тестам Lisp в 1985, и в то время эти сравнительные тесты также использовались с неCL, Шепелявит. Сам язык Common LISP был совершенно новым в 1985 - книга CLtL1, которая описала язык, был просто опубликован в 1984. Неудивительный реализации не были очень оптимизированы в то время. Реализованная оптимизация была в основном тем же (или меньше), который реализации прежде имели (как MacLisp).
, Но для [1 116] по сравнению с [1 117] основное различие - то, что код с помощью [1 118] легче понять для людей, так как обязательные пункты независимы друг от друга - тем более, что это - плохой стиль для использования в своих интересах левых к правильной оценке (не устанавливающий переменные как побочный эффект).
Я главным образом использую, ПОЗВОЛЯЮТ, если я specifgically не должен ПОЗВОЛИТЬ*, но иногда я пишу код, которому явно потребности ПОЗВОЛЯЮТ, обычно при выполнении различный (обычно сложный) установка по умолчанию. К сожалению, у меня нет удобного примера кода под рукой.
По-видимому, при помощи let
компилятор имеет больше гибкости для переупорядочения кода, возможно, для улучшений скорости или пространства.
Стилистически, с помощью параллельной привязки показывает намерение, что привязка группируется; это иногда используется в сохранении динамических связываний:
(let ((*PRINT-LEVEL* *PRINT-LEVEL*)
(*PRINT-LENGTH* *PRINT-LENGTH*))
(call-functions that muck with the above dynamic variables))
я иду один шаг вперед и использую , связывают , который объединяет let
, let*
, multiple-value-bind
, destructuring-bind
и т.д., и это даже расширяемо.
обычно мне нравится использовать "самую слабую конструкцию", но не с let
& друзья, потому что они просто дают шум коду (субъективность, предупреждающая! никакая потребность попытаться убедить меня в противоположном...)
Основное различие в общем Списке между ПОЗВОЛЕННЫМ и ПОЗВОЛИЛО*, то, что символы в ПОЗВОЛЕННОМ связываются параллельно, и в ПОЗВОЛЕННОМ* связываются последовательно. Используя ПОЗВОЛЕННЫЙ не позволяет init-формам выполняться параллельно, и при этом это не позволяет порядку init-форм быть измененным. причина состоит в том, что язык Common LISP позволяет функциям иметь побочные эффекты. Поэтому порядок оценки важен и всегда слева направо в форме. Таким образом, в ПОЗВОЛЕННОМ, init-формы оценены сначала, слева направо, затем привязка создается, слева направо параллельно. В ПОЗВОЛЕННОМ*, init-форма оценена и затем связана с символом в последовательности, слева направо.
В LISP часто существует требование использовать самые слабые конструкции. Некоторые руководства по стилю скажут Вам использовать =
, а не eql
, когда Вы будете знать, что сравненные объекты являются числовыми, например. Идея состоит в том, чтобы часто указывать то, что Вы имеете в виду, а не программируете компьютер эффективно.
Однако могут быть фактические улучшения эффективности высказывания только, что Вы имеете в виду, и не использование более сильных конструкций. Если у Вас есть инициализации с LET
, они могут быть выполнены параллельно, в то время как LET*
инициализации должны быть выполнены последовательно. Я не знаю, сделают ли какие-либо реализации на самом деле это, но некоторые могут хорошо в будущем.
Я приезжаю, перенося изобретенные примеры. Сравните результат этого:
(print (let ((c 1))
(let ((c 2)
(a (+ c 1)))
a)))
с результатом выполнения этого:
(print (let ((c 1))
(let* ((c 2)
(a (+ c 1)))
a)))
Вы не делаете , потребность ПОЗВОЛИЛА, но Вы обычно хотите она.
ПОЗВОЛЯЮТ, предполагает, что Вы просто делаете стандартную параллельную привязку ни с чем хитрое продолжение. ПОЗВОЛЬТЕ*, вызывает ограничения на компилятор и намекает пользователю, что существует причина, что необходима последовательная привязка. С точки зрения стиль , ПОЗВОЛЬТЕ, лучше, когда Вам не нужны дополнительные ограничения, введенные LET*.
может быть более эффективно использовать, ПОЗВОЛЯЮТ, чем ПОЗВОЛЕННЫЙ* (в зависимости от компилятора, оптимизатора, и т.д.):
(Вышеупомянутые пункты маркированного списка относятся к Схеме, другому диалекту LISP. clisp может отличаться.)
(let ((list (cdr list))
(pivot (car list)))
;quicksort
)
Конечно, это сработает:
(let* ((rest (cdr list))
(pivot (car list)))
;quicksort
)
И это:
(let* ((pivot (car list))
(list (cdr list)))
;quicksort
)
Но важна именно мысль.
В дополнение к ответу Rainer Joswig и с пуристской или теоретической точки зрения. Let и Let* представляют две парадигмы программирования; функциональную и последовательную соответственно.
Что касается того, почему я должен продолжать использовать Let* вместо Let, ну, вы лишаете меня удовольствия приходить домой и думать на чистом функциональном языке, в отличие от последовательного языка, с которым я провожу большую часть своего рабочего дня :)
.