Common Lisp의 LET 대 LET *


81

LET와 LET * (병렬 바인딩 대 순차 바인딩)의 차이점을 이해하고 있으며 이론적으로는 완벽하게 이해됩니다. 하지만 실제로 LET가 필요한 경우가 있습니까? 최근에 살펴본 모든 Lisp 코드에서 모든 LET를 변경없이 LET *로 바꿀 수 있습니다.

편집 : 좋아, 어떤 사람이 LET *를 매크로로 만들 었는지 이해 합니다. 내 질문은 LET *가 존재한다는 점을 감안할 때 LET가 주변에 머물러야하는 이유가 있습니까? LET *가 평범한 LET만큼 잘 작동하지 않는 실제 Lisp 코드를 작성 했습니까?

나는 효율성 논쟁을 사지 않는다. 첫째, LET *가 LET만큼 효율적인 것으로 컴파일 될 수있는 경우를 인식하는 것은 그렇게 어렵지 않은 것 같습니다. 둘째, CL 사양에는 효율성을 중심으로 설계된 것처럼 보이지 않는 많은 것들이 있습니다. (타입 선언이있는 LOOP를 마지막으로 본 게 언제입니까? 그것들이 사용되는 것을 본 적이 없다는 것을 알아 내기가 너무 어렵습니다.) 1980 년대 후반의 Dick Gabriel의 벤치 마크 이전에 CL 완전히 느 렸습니다.

이것은 이전 버전과의 호환성의 또 다른 경우 인 것 같습니다. 현명하게도 LET만큼 근본적인 것을 깨뜨릴 위험을 감수하고 싶지 않은 사람은 아무도 없습니다. 내 직감 이었지만, LET가 LET *보다 엄청나게 쉽게 많은 것들을 만들었던 내가 놓친 어리 석고 단순한 케이스가 아무도 없다는 소식을 들으면 위로가됩니다.


병렬은 잘못된 단어 선택입니다. 이전 바인딩 만 표시됩니다. 병렬 바인딩은 Haskell의 "... where ..."바인딩과 비슷합니다.
jrockway

나는 혼동하는 것을 목표로 삼지 않았습니다. 나는 이것이 사양에서 사용되는 단어라고 생각합니다. :-)
Ken

12
병렬이 정확합니다. 그것은 바인딩이 동시에 생기고 서로를 보지 않고 서로 그림자를주지 않는다는 것을 의미합니다. LET에 정의 된 일부 변수를 포함하지만 다른 변수는 포함하지 않는 사용자가 볼 수있는 환경이 존재하지 않습니다.
카즈

바인딩이 letrec과 더 비슷한 Haskells. 동일한 범위 수준에서 모든 바인딩을 볼 수 있습니다.
Edgar Klerks 2014 년

1
' let필요한 경우 가 있습니까?' '인수가 두 개 이상인 함수가 필요한 경우가 있습니까?'라고 묻는 것과 비슷합니다. let& let*어떤 효율성 개념 때문에 존재하지 않습니다. 프로그래밍 할 때 인간이 다른 인간에게 의도를 전달할 수 있기 때문에 존재합니다.
tfb

답변:


86

LET으로 대체 될 수 있기 때문에 그 자체는 함수형 프로그래밍 언어 에서 실제 원시가 아닙니다 LAMBDA. 이렇게 :

(let ((a1 b1) (a2 b2) ... (an bn))
  (some-code a1 a2 ... an))

~와 비슷하다

((lambda (a1 a2 ... an)
   (some-code a1 a2 ... an))
 b1 b2 ... bn)

그러나

(let* ((a1 b1) (a2 b2) ... (an bn))
  (some-code a1 a2 ... an))

~와 비슷하다

((lambda (a1)
    ((lambda (a2)
       ...
       ((lambda (an)
          (some-code a1 a2 ... an))
        bn))
      b2))
   b1)

어느 것이 더 간단한 지 상상할 수 있습니다. LET및 아닙니다 LET*.

LET코드를 더 쉽게 이해할 수 있습니다. 하나는 여러 개의 바인딩을보고 하나는 '효과'(리 바인딩)의 하향식 / 왼쪽-오른쪽 흐름을 이해할 필요없이 각 바인딩을 개별적으로 읽을 수 있습니다. 사용 LET*프로그래머 바인딩 독립 아니라는 것을 (코드를 읽는 일)에 신호를하지만, 다운 상단의 어떤 흐름이있다 - 일을 복잡하게한다.

Common Lisp에는 바인딩 값 LET이 왼쪽에서 오른쪽으로 계산 된다는 규칙이 있습니다. 함수 호출의 값이 평가되는 방식-왼쪽에서 오른쪽으로. 따라서 LET개념적으로 더 간단한 문장이며 기본적으로 사용해야합니다.

유형LOOP ? 꽤 자주 사용됩니다. 기억하기 쉬운 몇 가지 기본 형식 선언 형식이 있습니다. 예:

(LOOP FOR i FIXNUM BELOW (TRUNCATE n 2) do (something i))

위의 변수 ifixnum.

Richard P. Gabriel은 1985 년에 Lisp 벤치 마크에 대한 그의 책을 출판했으며 당시 이러한 벤치 마크는 비 CL Lisps에서도 사용되었습니다. Common Lisp 자체는 1985 년에 완전히 새로워졌습니다 . 언어를 설명 하는 CLtL1 책은 1984 년에 막 출판되었습니다. 그 당시 구현이 그다지 최적화되지 않은 것은 당연합니다. 구현 된 최적화는 기본적으로 이전 구현 (예 : MacLisp)과 동일 (또는 그 이하)이었습니다.

그러나 대한 LETLET*주요 차이점 사용하여 해당 코드는 LET이 측면으로 올바른 평가를 왼쪽 (설정하지 않는 변수를 활용하는 나쁜 스타일이 특히 때문에 - 바인딩 조항은 서로 독립적이기 때문에, 인간에 대한 이해하기 쉽게 효과).


3
아니 아니! Lambda는 LET로 대체 될 수 있기 때문에 실제 기본 요소가 아니며 인수 값을 얻기위한 API 만 제공하는 하위 수준의 람다는 다음과 같습니다. (low-level-lambda 2 (let ((x (car %args%)) (y (cadr args))) ...) :)
Kaz

36

LET 는 필요 하지 않지만 일반적으로 원합니다 .

LET는 까다로운 일이없이 표준 병렬 바인딩을 수행하고 있음을 제안합니다. LET *는 컴파일러에 대한 제한을 유도하고 사용자에게 순차 바인딩이 필요한 이유를 제안합니다. 스타일 측면에서 LET *에 의해 부과 된 추가 제한이 필요하지 않을 때 LET가 더 좋습니다.

LET *보다 LET를 사용하는 것이 더 효율적일 수 있습니다 (컴파일러, 최적화 프로그램 등에 따라 다름).

  • 병렬 바인딩은 병렬로 실행할 수 있습니다 (그러나 LISP 시스템이 실제로이를 수행하는지 여부는 알 수 없으며 init 양식은 여전히 ​​순차적으로 실행되어야합니다)
  • 병렬 바인딩은 모든 바인딩에 대해 하나의 새 환경 (범위)을 만듭니다. 순차 바인딩은 모든 단일 바인딩에 대해 새로운 중첩 환경을 만듭니다. 병렬 바인딩은 메모리를 적게 사용 하고 변수 조회더 빠릅니다 .

(위의 글 머리 기호는 Scheme, 다른 LISP 방언에 적용됩니다. clisp는 다를 수 있습니다.)


2
참고 : 첫 번째 풀렛 포인트가 오해의 소지가있는 이유에 대한 약간의 설명 은 이 답변 (및 / 또는 하이퍼 스펙의 링크 된 섹션)을 참조하십시오 . 바인딩은 병렬로 발생하지만, 형태는 순차적으로 실행된다 - 사양 당.
lindes

6
병렬 실행은 Common Lisp 표준이 어떤 식 으로든 처리하는 것이 아닙니다. 더 빠른 변수 조회도 신화입니다.
Rainer Joswig 2012

1
차이점은 컴파일러에게만 중요하지 않습니다. 나는 무슨 일이 일어나고 있는지에 대한 힌트로 let과 let *을 사용합니다. 내 코드에서 let을 보면 바인딩이 독립적이라는 것을 알 수 있으며 let *을 보면 바인딩이 서로 의존한다는 것을 알 수 있습니다. 그러나 나는 let과 let *을 일관되게 사용하기 때문에 그것을 압니다.
예레미야

28

나는 인위적인 예를 들었다. 이 결과를 비교하십시오.

(print (let ((c 1))
         (let ((c 2)
               (a (+ c 1)))
           a)))

이것을 실행 한 결과 :

(print (let ((c 1))
         (let* ((c 2)
                (a (+ c 1)))
           a)))

3
왜 이것이 사실인지 개발하는 데 관심이 있습니까?
John McAleely

4
@John : 첫 번째 예에서 a의 바인딩은의 외부 값을 나타냅니다 c. let*바인딩이 이전 바인딩을 참조하도록 허용 하는 두 번째 예에서 a의 바인딩은의 내부 값을 참조 c합니다. Logan은 이것이 인위적인 예라고 거짓말을하지 않으며 유용한 척도하지 않습니다. 또한 들여 쓰기는 비표준이며 오해의 소지가 있습니다. 둘 다에서 a's 바인딩은 c's 와 일치하도록 한 공간 이상 let이어야하며 내부의 '본체' 는 let자체 에서 두 공간 만 있어야 합니다.
zem

3
이 답변은 중요한 통찰력을 제공합니다. 하나는 것입니다 특별히 사용 let하나를하고자 할 때 (있는 나는 단순히 첫 번째를하지 의미) 보조 바인딩을 가진 바인딩 첫 번째 참조,하지만 당신은 은폐하기를 이전 바인딩 - 중 하나를 초기화 그것의 이전 값을 사용하여 귀하의 보조 바인딩.
lindes

2
들여 쓰기가 꺼져 있고 편집 할 수 없지만 이것이 지금까지 가장 좋은 예입니다. 내가 볼 때 (let ...) 바인딩이 서로 구축 되지 않고 별도로 처리 될 수 있음을 알고 있습니다. 내가 볼 때 (let * ...) 항상 조심스럽게 접근하고 어떤 바인딩이 재사용되고 있는지 매우주의 깊게 살펴 봅니다. 이러한 이유만으로도 중첩이 절대적으로 필요하지 않는 한 항상 (let)을 사용하는 것이 좋습니다.
andrew

1
(6 년 후 ...) 설계에 의해 잘못과 오해의 소지가 들여 쓰기,이었다로 구성 잡았다 ? 나는 그것을 고치기 위해 그것을 편집하는 경향이있다.
Will Ness

11

LISP에서는 가능한 가장 약한 구조를 사용하려는 욕구가 종종 있습니다. 예를 들어 일부 스타일 가이드는 비교 된 항목이 숫자라는 것을 알 =때보 다 사용하라고 알려줍니다 eql. 아이디어는 종종 컴퓨터를 효율적으로 프로그래밍하기보다는 의미하는 바를 지정하는 것입니다.

그러나 더 강력한 구조를 사용하지 않고 의미하는 바만 말하면 실제 효율성이 향상 될 수 있습니다. 를 사용한 초기화가있는 경우 LET병렬로 실행할 수 있지만 LET*초기화는 순차적으로 실행해야합니다. 어떤 구현이 실제로 그렇게할지 모르겠지만 일부는 미래에 잘 될 수 있습니다.


2
좋은 지적. Lisp는 고수준 언어이기 때문에 왜 "가장 약한 구조"가 Lisp 땅에서 원하는 스타일인지 궁금합니다. Perl 프로그래머가 "글쎄요, 여기서 정규 표현식을 사용할 필요 가 없습니다 ..." 라고 말하는 것을 볼 수 없습니다. :-)
Ken

1
잘 모르겠지만 확실한 스타일 선호도가 있습니다. 가능한 한 동일한 형식을 사용하려는 사람들 (나와 같은)에 의해 다소 반대됩니다 (저는 setf 대신 setq를 거의 작성하지 않습니다). 당신이 의미하는 바를 말하는 아이디어와 관련이있을 수 있습니다.
David Thornley

=운영자는도 강한 나보다 약한이다 eql. 와 0같기 때문에 더 약한 테스트 0.0입니다. 그러나 숫자가 아닌 인수가 거부되기 때문에 더 강력합니다.
Kaz

2
당신이 언급하고있는 원칙 은 가장 약한 것이 아니라 적용 가능한 가장 강력한 프리미티브 를 사용하는 것 입니다. 예를 들어 비교 대상이 기호 인 경우 . 또는 상징적 인 장소에 할당하고 있다는 것을 알고 있다면 . 그러나이 원칙은 조기 최적화없이 고급 언어를 원하는 많은 Lisp 프로그래머도 거부합니다. eqsetq
Kaz 2012

1
실제로 CLHS는 " 바인딩 이 병렬로 수행됩니다"라고 말하지만 " init-form-1 , init-form-2 등 의 표현식 은 [특정, 왼쪽에서 오른쪽 (또는 상단 -down)] order ". 따라서 값은 순차적으로 계산되어야합니다 ( 모든 값이 계산 된 바인딩이 설정 됨 ). RPLACD와 같은 구조 돌연변이는 언어의 일부이고 진정한 병렬 처리로 비결정 적이기 때문에 의미가 있습니다.
Will Ness

9

저는 최근에 두 인수의 함수를 작성했는데, 어떤 인수가 더 큰지 알면 알고리즘이 가장 명확하게 표현됩니다.

(defun foo (a b)
  (let ((a (max a b))
        (b (min a b)))
    ; here we know b is not larger
    ...)
  ; we can use the original identities of a and b here
  ; (perhaps to determine the order of the results)
  ...)

치죠는 b우리가 사용했던 경우, 크다 let*, 우리가 실수로 설정 한 것 ab같은 값에.


1
나중에 outer 내부에서 x 및 y 값이 필요하지 않으면 다음을 사용하여 let보다 간단하고 명확하게 수행 할 수 있습니다 (rotatef x y).
Ken

사실입니다. x와 y가 특수 변수 인 경우 더 유용 할 수 있습니다.
Samuel Edwin Ward

9

LET와 LET *의 공통 목록의 주요 차이점은 LET의 기호는 병렬로 바인딩되고 LET *의 기호는 순차적으로 바인딩된다는 것입니다. LET를 사용하면 init-form을 병렬로 실행할 수 없으며 init-form의 순서를 변경할 수 없습니다. 그 이유는 Common Lisp가 함수가 부작용을 갖도록 허용하기 때문입니다. 따라서 평가 순서는 중요하며 양식 내에서 항상 왼쪽에서 오른쪽입니다. 따라서 LET에서는 init-form이 먼저 왼쪽에서 오른쪽으로 평가 된 다음 바인딩이 왼쪽 에서 오른쪽 으로 병렬로 생성됩니다. LET *에서 init-form은 평가 된 다음 왼쪽에서 오른쪽으로 순서대로 기호에 바인딩됩니다.

CLHS : 특수 연산자 LET, LET *


2
이 대답은 아마도에 대한 응답 인에서 약간의 에너지를 그리는 것 같습니다 이 답변 ? 또한 연결된 사양에 따라 바인딩LETinit-form이 직렬로 실행된다는 것을 호출하는 것이 정확하더라도 에서 병렬로 수행 된다고합니다. 그것이 현존하는 구현에 실질적인 차이가 있는지 여부는 모르겠습니다.
lindes

8

나는 한 단계 더 사용 갈 바인드 통합하여 그 let, let*, multiple-value-bind, destructuring-bind등, 그리고 심지어 확장의를.

일반적으로 나는 "가장 약한 구조"를 사용하는 것을 좋아하지만 let& 친구들 과는 그렇지 않습니다. 그들은 단지 코드에 소음을주기 때문입니다 (주관성 경고! 그 반대를 설득 할 필요가 없습니다 ...).


오, 깔끔 해요. 이제 BIND와 놀러 갈 게요. 링크 주셔서 감사합니다!
Ken

4

아마도 let컴파일러 를 사용 하면 공간이나 속도 향상을 위해 코드를 더 유연하게 재정렬 할 수 있습니다.

스타일 적으로 병렬 바인딩을 사용하면 바인딩이 함께 그룹화되는 의도를 알 수 있습니다. 이것은 때때로 동적 바인딩을 유지하는 데 사용됩니다.

(let ((*PRINT-LEVEL* *PRINT-LEVEL*)
      (*PRINT-LENGTH* *PRINT-LENGTH*))
  (call-functions that muck with the above dynamic variables)) 

코드의 90 %에서, 당신이 사용하는지 여부 차이도하지 않습니다 LET또는 LET*. 따라서를 사용 *하면 불필요한 글리프를 추가하는 것입니다. 경우 LET*병렬 바인더이고 LET시리얼 바인더했다 프로그래머는 여전히 사용하는 것이 LET, 만 뽑아 LET*결합 병렬을 원하는 경우. 이것은 LET*드물게 만들 것 입니다.
Kaz는

실제로 CLHS 는 let 's init-form 의 평가 순서지정합니다 .
Will Ness

4
(let ((list (cdr list))
      (pivot (car list)))
  ;quicksort
 )

물론 이것은 작동합니다.

(let* ((rest (cdr list))
       (pivot (car list)))
  ;quicksort
 )

이:

(let* ((pivot (car list))
       (list (cdr list)))
  ;quicksort
 )

그러나 중요한 것은 생각입니다.


2

OP는 "실제로 LET가 필요했습니다"라고 묻습니다.

Common Lisp가 만들어 졌을 때 여러 방언으로 된 기존 Lisp 코드의 보트로드가있었습니다. Common Lisp를 설계 한 사람들이 받아 들인 요약은 공통 기반을 제공 할 Lisp의 방언을 만드는 것이 었습니다. 그들은 기존 코드를 Common Lisp로 포팅하는 것을 쉽고 매력적으로 만드는 것이 "필요"했습니다. LET 또는 LET *를 언어에서 제외하면 다른 장점이있을 수 있지만 그 핵심 목표는 무시했을 것입니다.

나는 데이터 흐름이 어떻게 전개되고 있는지 독자에게 알려주기 때문에 LET *보다 LET를 선호합니다. 내 코드에서 적어도 LET *가 표시되면 초기에 바인딩 된 값이 나중에 바인딩에 사용된다는 것을 알고 있습니다. 나는 그것을 "필요"합니까? 하지만 도움이된다고 생각합니다. 나는 드물게 LET *로 기본 설정된 코드와 LET의 모양이 작성자가 정말로 원했다는 신호를 읽었습니다. 예를 들어 두 변수의 의미를 바꿉니다.

(let ((good bad)
     (bad good)
...)

'실제적 필요'에 접근하는 논란의 여지가있는 시나리오가 있습니다. 그것은 매크로에서 발생합니다. 이 매크로 :

(defmacro M1 (a b c)
 `(let ((a ,a)
        (b ,b)
        (c ,c))
    (f a b c)))

보다 잘 작동

(defmacro M2 (a b c)
  `(let* ((a ,a)
          (b ,b)
          (c ,c))
    (f a b c)))

(M2 cba)는 잘되지 않을 테니까요. 그러나 이러한 매크로는 여러 가지 이유로 매우 조잡합니다. 그래서 '실제적 필요'주장을 약화시킵니다.


1

Rainer Joswig의 답변 외에도 순수 주의적 또는 이론적 관점에서. Let & Let *은 두 가지 프로그래밍 패러다임을 나타냅니다. 각각 기능적 및 순차적.

왜 내가 Let 대신 Let *을 계속 사용해야하는지에 관해서는, 글쎄, 당신은 내가 집으로 돌아와서 내가 하루의 대부분을 작업하는 순차 언어와는 달리 순수한 기능적 언어로 생각하면서 재미를 취하고 있습니다. :)


1

Let you use parallel binding,

(setq my-pi 3.1415)

(let ((my-pi 3) (old-pi my-pi))
     (list my-pi old-pi))
=> (3 3.1415)

Let * 직렬 바인딩을 사용하면

(setq my-pi 3.1415)

(let* ((my-pi 3) (old-pi my-pi))
     (list my-pi old-pi))
=> (3 3)

예, 그것이 정의 된 방법입니다. 그러나 전자는 언제 필요합니까? 특정 순서로 pi 값을 변경해야하는 프로그램을 실제로 작성하는 것이 아닙니다. :-)

1

let운영자가 지정하는 모든 바인딩을위한 단일 환경을 소개합니다. let*, 적어도 개념적으로 (그리고 우리가 잠시 선언을 무시한다면) 여러 환경을 도입합니다 :

즉 말하자면:

(let* (a b c) ...)

처럼:

(let (a) (let (b) (let (c) ...)))

따라서 어떤 의미에서는 let더 원시적이지만 -s let*의 캐스케이드를 작성하기위한 구문 설탕입니다 let.

하지만 신경 쓰지 마세요. (그리고 나는 우리가 왜 "신경 쓰지 말아야"하는지에 대해 나중에 아래에서 정당화 할 것입니다). 사실 두 개의 연산자가 있고 코드의 "99 %"에서 어떤 연산자를 사용하든 상관 없습니다. 선호하는 이유 let이상은 let*그것이이없는 것을 단순히 *끝에 매달려 있습니다.

두 개의 연산자가 있고 하나는 *이름에 매달려 있을 때마다 *해당 상황에서 작동 하는 경우 없는 연산자를 사용 하여 코드를 덜 추하게 유지하십시오.

그게 전부입니다.

그 존재는 내가 경우 의심 말했다 letlet*그 의미를 교환, 가능성이 사용하는 것이 더 희귀 한 것 let*지금보다. 직렬 바인딩 동작은이를 필요로하지 않는 대부분의 코드를 괴롭히지 않습니다. 거의 let*병렬 동작을 요청하는 데 사용되어야하는 경우가 거의 없으며 이러한 상황은 섀도 잉을 피하기 위해 변수 이름을 변경하여 수정할 수도 있습니다.

이제 그 약속 된 논의를 위해. let*개념적으로 여러 환경을 소개 하지만 let*단일 환경이 생성되도록 구성 을 컴파일하는 것은 매우 쉽습니다 . 그래서 (적어도 우리는 ANSI CL 선언을 무시하면) 되었더라도 한 것을 대수 평등이 let*다수의 중첩에 해당하는 let차종은 -s, let원시적보고는, 실제로 확장에 이유가 없다 let*let컴파일하는이, 심지어는 나쁜 생각.

한 가지 더 : Common Lisp는 lambda실제로 let*유사 의미론을 사용합니다 ! 예:

(lambda (x &optional (y x) (z (+1 y)) ...)

여기서 xinit-form y은 이전 x매개 변수 에 액세스 하며 유사하게 (+1 y)이전 y옵션을 참조합니다 . 이 언어 영역에서는 바인딩의 순차적 가시성에 대한 명확한 선호도가 나타납니다. 의 양식 lambda이 매개 변수를 볼 수 없으면 유용 하지 않습니다. 선택적 매개 변수는 이전 매개 변수의 값과 관련하여 간결하게 기본값을 설정할 수 없습니다.


0

에서 let모든 변수 초기화 표현식은 정확히 동일한 어휘 환경, 즉 let. 이러한 표현이 어휘 폐쇄를 포착하는 경우 모두 동일한 환경 객체를 공유 할 수 있습니다.

에서 let*모든 초기화 표현식은 다른 환경에 있습니다. 연속 된 각 식에 대해 새 식을 만들기 위해 환경을 확장해야합니다. 적어도 추상 의미론에서 클로저가 캡처되면 다른 환경 객체를 갖습니다.

let*일상적인 대체물로 적합하려면 불필요한 환경 확장을 축소하도록 A 를 잘 최적화해야합니다 let. 어떤 형식이 무엇에 액세스하고 모든 독립적 인 형식을 더 큰 결합 된 형식으로 변환하는 컴파일러가 있어야합니다 let.

(이는 let*계단식 let형식 을 생성하는 매크로 연산자 일 경우에도 마찬가지입니다 . 최적화는 계단식 lets 에서 수행됩니다 ).

적절한 범위 지정의 부족이 드러나기 때문에 초기화를 수행하기 위해 숨겨진 변수 할당을 사용 let*하여 단일 순진한으로 구현할 수 없습니다 let.

(let* ((a (+ 2 b))  ;; b is visible in surrounding env
       (b (+ 3 a)))
  forms)

이것이로 설정되면

(let (a b)
  (setf a (+ 2 b)
        b (+ 3 a))
  forms)

이 경우에는 작동하지 않습니다. 안쪽 b은 바깥 쪽을 섀도 잉 b하므로 결국 2를 nil. 이러한 모든 변수의 이름을 알파로 변경하면 이러한 종류의 변환 수행 할 수 있습니다. 그런 다음 환경이 멋지게 평면화됩니다.

(let (#:g01 #:g02)
  (setf #:g01 (+ 2 b) ;; outer b, no problem
        #:g02 (+ 3 #:g01))
  alpha-renamed-forms) ;; a and b replaced by #:g01 and #:g02

이를 위해 디버그 지원을 고려해야합니다. 프로그래머가 디버거이 어휘 범위로 단계를하는 경우, 우리가 다루고 싶어 #:g01대신 a.

따라서 기본적으로 .NET으로 줄일 수있는 경우 let*뿐만 아니라 수행하기 위해 잘 최적화되어야하는 복잡한 구조입니다 .letlet

혼자 호의를 정당화 할 것이라는 점 let이상 let*. 좋은 컴파일러가 있다고 가정 해 봅시다. 왜 let*항상 사용하지 않습니까?

일반적인 원칙으로, 우리는 오류가 발생하기 쉬운 하위 수준의 구성보다 생산성을 높이고 실수를 줄이는 상위 수준의 구성을 선호해야하며, 희생 할 필요가 거의 없도록 상위 수준 구성의 우수한 구현에 가능한 한 많이 의존해야합니다. 성능을 위해 사용합니다. 이것이 우리가 처음에 Lisp와 같은 언어로 작업하는 이유입니다.

그 이유는 잘 적용되지 않습니다 letlet*하기 때문에, let*분명히에 높은 수준의 추상화 상대하지 않습니다 let. 그들은 "동등한 수준"에 관한 것입니다. 를 사용하면 let*간단히으로 전환하여 해결되는 버그를 도입 할 수 있습니다 let. 그리고 그 반대 . let*실제로는 let중첩 을 시각적으로 축소하기위한 가벼운 구문 설탕 일 뿐이며 중요한 새로운 추상화는 아닙니다.


-1

나는 특별히 LET *가 필요하지 않는 한 주로 LET를 사용하지만, 가끔은 일반적으로 분류 된 (일반적으로 복잡한) 디폴트를 수행 할 때 명시 적으로 LET가 필요한 코드를 작성합니다 . 불행히도 편리한 코드 예제가 없습니다.


-1

누가 letf 대 letf *를 다시 작성하고 싶습니까? 해제 방지 통화 수?

순차 바인딩을 더 쉽게 최적화 할 수 있습니다.

어쩌면 환경에 영향을 미칠 까요?

동적 범위로 연속을 허용합니까?

가끔 (let (xyz) (setq z 0 y 1 x (+ (setq x 1) (prog1 (+ xy) (setq x (1- x))))) (values ​​()))

요점은 간혹 간결할수록 읽기 쉽다는 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.