LET, LET* 특수 연산자

LET, LET* 특수 연산자 (let, let*)

letlet*은 Common Lisp의 특수 연산자로, 새 지역 변수 바인딩을 만들고 그 바인딩을 쓰는 폼들을 실행해요. let은 바인딩들을 병렬(parallel)로, let*순차적(sequentially)으로 수행한다는 차이가 있어요. Lisp에서 변수를 도입하는 가장 기본적인 블록 구조예요.

출처: CLHS: Special Operator LET, LET*

문법 (Syntax)

let ({var | (var [init-form])}*) declaration* form* => result*
let* ({var | (var [init-form])}*) declaration* form* => result*

인자와 값 (Arguments and Values)

  • var — 기호(symbol)예요.
  • init-form — 폼이에요.
  • declarationdeclare 표현식이며, 평가되지 않아요.
  • form — 폼이에요.
  • resultsform들이 돌려준 값들이에요.

설명 (Description)

letlet*은 새 변수 바인딩을 만들고 그 바인딩을 쓰는 폼들을 실행해요. let은 바인딩을 병렬로, let*은 순차적으로 수행해요.

다음 폼은

 (let ((var1 init-form-1)
       (var2 init-form-2)
       ...
       (varm init-form-m))
   declaration1
   declaration2
   ...
   declarationp
   form1
   form2
   ...
   formn)

먼저 init-form-1, init-form-2 등을 그 순서대로 평가해서 값을 저장해요. 그다음 모든 변수 varj를 대응하는 값에 바인딩해요. 각 바인딩은 반대의 special 선언이 없으면 어휘적(lexical)이에요. 그다음 formk를 순서대로 평가하는데, 마지막을 제외한 값들은 버려져요(즉 let의 몸체는 암시적 progn이에요).

let*let과 비슷한데, 변수들의 바인딩을 병렬이 아니라 순차적으로 수행해요. 어떤 varinit-form 표현식은 let*에서 앞서 바인딩된 var들을 참조할 수 있어요.

다음 폼은

 (let* ((var1 init-form-1)
        (var2 init-form-2)
        ...
        (varm init-form-m))
   declaration1
   declaration2
   ...
   declarationp
   form1
   form2
   ...
   formn)

먼저 init-form-1을 평가하고 변수 var1을 그 값에 바인딩해요. 그다음 init-form-2를 평가하고 var2를 바인딩하는 식으로 이어져요. 그다음 formj를 순서대로 평가하는데, 마지막을 제외한 값들은 버려져요(즉 let*의 몸체는 암시적 progn이에요).

letlet* 모두, 어떤 varinit-form이 없으면 varnil로 초기화돼요.

let은 이름 바인딩의 유효 범위가 어떤 초기값 폼도 포함하지 않는다는 성질이 있어요. 반면 let*에서는 어떤 변수의 유효 범위가 그 이후 변수 바인딩들을 위한 나머지 초기값 폼들까지 포함해요.

예제 (Examples)

 (setq a 'top) => TOP
 (defun dummy-function () a) => DUMMY-FUNCTION
 (let ((a 'inside) (b a))
   (format nil "~S ~S ~S" a b (dummy-function))) => "INSIDE TOP TOP" 
 (let* ((a 'inside) (b a))
   (format nil "~S ~S ~S" a b (dummy-function))) => "INSIDE INSIDE TOP" 
 (let ((a 'inside) (b a))
   (declare (special a))
   (format nil "~S ~S ~S" a b (dummy-function))) => "INSIDE TOP INSIDE"

첫 예시에서 (b a)a는 let 바인딩이 시작되기 전의 전역 a를 가리켜요(병렬이라서). 그래서 결과가 "INSIDE TOP TOP"이에요. 두 번째 let*에서는 b가 이미 바인딩된 a를 보므로 "INSIDE INSIDE TOP". 세 번째는 aspecial로 선언해서 dummy-function이 전역 특별 변수 a를 보게 만든 결과예요.

다음 코드는 틀렸어요.

 (let (x)
   (declare (integer x))
   (setq x (gcd y z))
   ...)

x가 쓰이기 전에 분명히 설정되고, 선언된 타입 integer의 값으로 설정되긴 하지만, 그럼에도 x는 초기에 nil 값을 갖는데 이는 타입 선언을 위반해요.

영향 (Affected By)

없음.

예외 상황 (Exceptional Situations)

없음.

함께 보기 (See Also)

  • progv

참고 (Notes)

없음.

더 알아보기 (Learn more)