prog, prog* — 변수 바인딩과 goto 기반 블록

prog, prog* — 변수 바인딩과 goto 기반 블록

오래된 구조적 프로그래밍 스타일처럼, 지역 변수를 묶고 returngo로 제어를 옮기고 싶을 때가 있어요. progprog*매크로로, 변수 바인딩·return·go라는 세 가지 동작을 한 폼에 합쳐 놓아요.

출처: PROG, PROG* - Common Lisp HyperSpec

시그니처

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

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

본문

인자와 값

  • var — 변수 이름이에요.
  • init-form이에요.
  • declarationdeclare 표현식이고, 평가되지 않아요.
  • taggo 태그이고, 평가되지 않아요.
  • statementcompound form이고, 아래에서 설명하는 대로 평가돼요.
  • results정상 반환(normal return) 이 일어나면 nil, 명시적 반환(explicit return) 이 일어나면 전달된 값들이에요.

설명

progprog*는 세 가지 서로 다른 동작을 수행해요: 지역 변수를 묶고, return 문을 쓸 수 있게 하고, go 문을 쓸 수 있게 해요. 전형적인 prog는 이렇게 생겼어요:

(prog (var1 var2 (var3 init-form-3) var4 (var5 init-form-5))
      declaration*
      statement1
 tag1
      statement2
      statement3
      statement4
 tag2
      statement5
      ...
      )

prog의 경우, init-form들이 먼저 주어진 순서대로 평가돼요. 그런 다음 var들이 해당 값에 병렬로(parallel) 묶여요. 어떤 varinit-form이 주어지지 않으면 그 varnil로 묶여요.

prog의 몸통은 tagbody 처럼 실행돼요. go 문으로 제어를 tag로 옮길 수 있고, tagstatement를 표시해요.

prog는 전체 주위에 암묵적으로 nil이라는 이름의 블록을 만들기 때문에, 언제든 return으로 prog 을 빠져나갈 수 있어요.

progprog*의 차이는, prog*에서는 var들의 바인딩과 초기화순차적으로 이루어져서 각 init-form이 앞선 값들을 사용할 수 있다는 점이에요.

예제

(prog* ((y z) (x (car y)))
       (return x))

이것은 z의 값의 car를 돌려줘요.

(setq a 1) =>  1
(prog ((a 2) (b a)) (return (if (= a b) '= '/=))) =>  /=
(prog* ((a 2) (b a)) (return (if (= a b) '= '/=))) =>  =
(prog () 'no-return-value) =>  NIL
(defun king-of-confusion (w)
  "Take a cons of two lists and make a list of conses.
   Think of this function as being like a zipper."
  (prog (x y z)          ;Initialize x, y, z to NIL
       (setq y (car w) z (cdr w))
 loop
       (cond ((null y) (return x))
             ((null z) (go err)))
 rejoin
       (setq x (cons (cons (car y) (car z)) x))
       (setq y (cdr y) z (cdr z))
       (go loop)
 err
       (cerror "Will self-pair extraneous items"
               "Mismatch - gleep!  ~S" y)
       (setq z y)
       (go rejoin))) =>  KING-OF-CONFUSION

위 함수는 다음과 같이 더 명료하게 쓸 수도 있어요:

(defun prince-of-clarity (w)
  "Take a cons of two lists and make a list of conses.
   Think of this function as being like a zipper."
  (do ((y (car w) (cdr y))
       (z (cdr w) (cdr z))
       (x '() (cons (cons (car y) (car z)) x)))
      ((null y) x)
    (when (null z)
      (cerror "Will self-pair extraneous items"
              "Mismatch - gleep!  ~S" y)
      (setq z y)))) =>  PRINCE-OF-CLARITY

Affected By

없음.

Notes

progblock, let, tagbody로 다음과 같이 설명할 수 있어요:

(prog variable-list declaration . body)
   ==  (block nil (let variable-list declaration (tagbody . body)))

더 알아보기

  • block — 명명된 블록
  • let — 변수 바인딩
  • tagbody — 태그 기반 제어 흐름
  • go — 태그로 이동
  • return — 블록에서 반환
  • Section 3.1 (Evaluation)