common-lisp
양식 그룹화
수색…
그룹화는 언제 필요합니까?
Common Lisp의 일부 장소에서는 일련의 양식이 순서대로 평가됩니다. 예를 들어, defun 또는 lambda 의 본문이나 점선 의 본문에서. 이 경우 여러 양식을 순서대로 작성하면 예상대로 작동합니다. 그러나 if 표현식의 then 과 else 부분과 같은 몇 군데에서는 단 하나의 형식 만 허용됩니다. 물론, 그 장소에서 여러 표현식을 실제로 평가할 수도 있습니다. 이러한 상황에서 암묵적인 명시적인 그룹화 형식이 필요합니다.
예측
범용 특수 연산자 progn 은 0 개 이상의 형식을 평가하는 데 사용됩니다. 마지막 형식의 값이 반환됩니다. 예를 들어, 다음에서 (print 'hello) 가 평가되고 (그 결과는 무시된다), 42 가 평가되고 그 결과 ( 42 )가 반환된다 :
(progn
(print 'hello)
42)
;=> 42
progn에 형식이 없으면 nil 이 반환됩니다.
(progn)
;=> NIL
일련의 양식을 그룹화하는 것 외에도 progn 은 progn 양식이 최상위 양식 인 경우 모든 양식이 최상위 양식 으로 처리된다는 중요한 특성이 있습니다. 이것은 최상위 양식으로 모두 처리해야하는 여러 양식으로 확장되는 매크로를 작성할 때 중요 할 수 있습니다.
Progn 은 마지막 형식의 모든 값을 반환한다는 점에서 가치가 있습니다. 예를 들어,
(progn
(print 'hello)
(values 1 2 3))
;;=> 1, 2, 3
대조적으로, 일부 그룹화 표현식은 결과 생성 양식의 기본 값만 리턴합니다.
암시 적 예측
어떤 형태는 암묵적인 예언 을 사용하여 행동을 묘사합니다. 예를 들어, 폼의 경우 본질적으로 일방적 인 when 과 unless 매크로는 암시적인 progn의 관점에서 동작을 설명합니다. 이것은 다음과 같은 형식을 의미합니다.
(when (foo-p foo)
form1
form2)
(foo-p foo) 조건이 참이면 form1 과 form2 가 progn에 포함 된 것처럼 그룹화됩니다. when 매크로의 확장은 본질적으로 다음과 같습니다.
(if (foo-p foo)
(progn
form1
form2)
nil)
Prog1 및 Prog2
종종 여러 표현식을 평가하고 마지막 표현식이 아닌 첫 번째 또는 두 번째 형식의 결과를 반환하는 것이 도움이됩니다. let 및을 사용하여 쉽게 수행 할 수 있습니다 . 예를 들면 다음과 같습니다.
(let ((form1-result form1))
form2
form3
;; ...
form-n-1
form-n
form1-result)
이 형식은 일부 응용 프로그램에서 일반적이므로 Common Lisp에는 progn 과 같은 prog1 과 prog2 가 포함되지만 각각 첫 번째와 두 번째 형식의 결과가 반환됩니다. 예를 들면 :
(prog1
42
(print 'hello)
(print 'goodbye))
;; => 42
(prog2
(print 'hello)
42
(print 'goodbye))
;; => 42
PROG1 / PROG2 및 progn 간의 중요한 차이점은, 그러나, PROG1 및 PROG2에만 제 1 및 제 2 형태의 기본 값을 반환하는 반면 progn가 최종 형태의 모든 값을 반환한다. 예를 들면 :
(progn
(print 'hello)
(values 1 2 3))
;;=> 1, 2, 3
(prog1
(values 1 2 3)
(print 'hello))
;;=> 1 ; not 1, 2, 3
prog1 스타일 평가를 사용하는 여러 값의 경우 대신 multiple-value-prog1을 사용하십시오. 비슷한 다중 가치 prog2 는 없지만 필요한 경우 구현하기가 어렵지 않습니다.
블록
특수 연산자 블록 은 암시 적 progn
과 같은 여러 Lisp 양식을 그룹화 할 수 있으며 블록의 이름 을 지정하기 위해 이름을 사용합니다. 블록 내의 양식을 평가할 때 특수 연산자 return-from 을 사용하여 블록을 떠날 수 있습니다. 예를 들면 :
(block foo
(print 'hello) ; evaluated
(return-from foo)
(print 'goodbye)) ; not evaluated
;;=> NIL
반환 값은 반환 값과 함께 제공 될 수도 있습니다.
(block foo
(print 'hello) ; evaluated
(return-from foo 42)
(print 'goodbye)) ; not evaluated
;;=> 42
명명 된 블록은 코드 덩어리가 의미있는 이름을 가질 때 또는 블록이 중첩 될 때 유용합니다. 어떤 맥락에서는 블록에서 일찍 돌아올 수있는 능력 만 중요합니다. 이 경우 블록 이름으로 nil 을 사용하여 리턴 할 수 있습니다. return 은 블록 이름이 항상 nil 이라는 점만 제외하면 return-from 과 같습니다.
참고 : 동봉 된 양식은 최상위 양식이 아닙니다. 이것은 progn
과 다르다. 여기서 둘러싸인 형태의 최상위 progn
형식은 여전히 최상위 형태로 간주된다.
태그 바디
그룹 폼에서 많은 제어를 위해 tagbody 특수 연산자가 매우 유용 할 수 있습니다. tagbody 폼 내부의 폼은 go 태그 (그냥 기호 또는 정수) 또는 실행할 폼입니다. tagbody 내에서 go 특수 연산자는 실행을 새 위치로 전송하는 데 사용됩니다. 이러한 유형의 프로그래밍은 임의의 실행 경로를 허용하기 때문에 상당히 낮은 수준으로 간주 될 수 있습니다. 다음은 for-loop을 tagbody 로 구현했을 때의 모습을 보여주는 자세한 예입니다.
(let (x) ; for (x = 0; x < 5; x++) { print(hello); }
(tagbody
(setq x 0)
prologue
(unless (< x 5)
(go end))
begin
(print (list 'hello x))
epilogue
(incf x)
(go prologue)
end))
tagbody 와 go 는 일반적으로 "GOTO가 유해하다고 생각하기"때문에 일반적으로 사용되지 않지만 상태 시스템과 같은 복잡한 제어 구조를 구현할 때는 도움이 될 수 있습니다. 많은 반복 구조 또한 암시적인 태그 바디 로 확장됩니다. 예를 들어, 점들 (dotimes )의 본문은 일련의 태그 및 양식으로 지정됩니다.
어떤 양식을 사용해야합니까?
그룹화와 관련된 양식으로 확장되는 매크로를 작성할 때 어떤 그룹화 구성을 확장 할 것인지 고려하는 것이 좋습니다.
정의 스타일 형태의 경우, 예를 들면, 정의 - 위젯 등 일반적으로 최상위 형태로 나타납니다 매크로, 그리고 여러 defun는 s의 defstruct들, 보통 그 자식 폼이 있도록하는 progn를 사용하는 것이 합리적이다 최상위 양식으로 처리됩니다. 반복 양식의 경우 암시적인 태그 바디 가 더 일반적입니다.
예를 들어,의 몸 dotimes , DOLIST , 그리고 어떻게 각각 암시 tagbody로 확장합니다.
코드의 명명 된 "덩어리"를 정의하는 양식의 경우 암시 적 블록 이 유용 할 때가 많습니다. 예를 들어, defun 본문은 암시 적 progn 내부에 있지만 암시 적 progn 은 함수 이름을 공유하는 블록 내에 있습니다. 즉, return-from 을 사용하여 함수에서 빠져 나올 수 있습니다. 그런 광고문