(Lispex)sicp.io
제3장 · 점검

저장 위치를 따라가고 계산을 미루고 필요한 연결만 바꾸세요.

정본 수업 프로그램 열두 개로 상태와 시간, 지연된 계산, 가변 자료구조, 내부 정의를 다시 연결합니다.

점검 질문

다음 호출까지 남는 바인딩과 아직 일어나지 않은 계산, 변경되는 연결을 구분할 수 있나요?

  • 상태 클로저의 대입 따라가기
  • 비공개 저장 위치 구분하기
  • 처음 force와 메모이즈된 force 구분하기
  • 스트림에서 유한한 부분만 요구하기
  • 큐의 앞·뒤 연결 따라가기
  • 같은 시드에서 같은 전이 재현하기
  • 제약을 통해 빠진 값 유도하기
  • 시뮬레이션 시간으로 사건 정렬하기
  • 오래된 읽기에서 갱신 손실 찾기
  • serializer로 상태 변경 순서 정하기
  • 서로 재귀적인 내부 도우미 따라가기
  • 기존 테이블 레코드 갱신하고 새 레코드 연결하기

지역 상태와 비공개 환경은 호출 뒤에도 남는 바인딩으로 이력을 드러냅니다. promise와 스트림은 아직 계산하지 않은 일을 요구 시점까지 보존합니다.

큐와 테이블은 전체 구조가 아니라 필요한 연결만 바꿉니다. 내부 정의는 같은 호출 환경의 여러 프로시저가 협력하면서 이름을 바깥에 노출하지 않는 방식을 보여 줍니다.

시드, 제약 네트워크, 사건 목록, serializer는 난수 상태와 지식과 시간과 갱신 순서를 명시적인 데이터와 프로시저로 나타냅니다.

리스펙스 · SICP 코드SICP에 필요한 Scheme 호환 문법을 리스펙스 SICP 프로필로 실행합니다.
(let ((balance 100))
  (let ((withdraw
         (lambda (amount)
           (if (>= balance amount)
               (begin
                 (set! balance (- balance amount))
                 balance)
               'insufficient-funds))))
    (let ((first (withdraw 25)))
      (let ((second (withdraw 25)))
        (let ((third (withdraw 60)))
          (list first second third))))))
리스펙스 학습용 런타임리스펙스 SICP 프로필 1.0.0
리스펙스 SICP 런타임 불러오는 중
리스펙스 · SICP 코드UTF-8 384 / 1,048,576바이트
예제
결과
출력
진단
보이는 실행 흐름0 / 0 개의 실행 이벤트
    이 브라우저 결과는 리스펙스 바우치나 권한이 아닙니다.wasm —
    예상 관찰

    각 프로그램은 대응하는 제3장 수업의 첫 예상 관찰을 반환합니다. 내부 정의는 ((#f #t) (#t #f))를, 한 키 테이블은 (9 5 #f (*table* (beta . 5) (alpha . 9)))를 반환합니다.

    실행 흐름에서 볼 점

    대입, force, 스트림 꼬리, 큐 링크, 시드, 커넥터 알림, 사건 삽입, 잔액 읽기와 쓰기, 지역 도우미 호출, assoc 순회, set-cdr! 대상을 차례로 찾으세요.

    답을 보기 전에 설명하기

    상태와 시간과 가변 구조를 생각하는 질문 열두 개

    답이 이전 호출에 의존하기 시작하면 무엇이 달라질까요?

    Three nested single-binding let expressions make the sequence explicit. Each result therefore reflects the state left by the preceding call instead of starting again from 100.

    같은 프로시저가 만든 두 카운터가 서로의 값을 덮어쓰지 않는 이유는 무엇일까요?

    The second example moves value outside both procedures. That single location is then shared, so an update through either procedure becomes the starting point for the other.

    force를 두 번 호출해도 계산이 한 번만 일어나는 이유는 무엇일까요?

    The second force returns the stored value without running the body again. The final calls value remains 1, making memoization visible in the result.

    끝이 없는 수열을 유한한 실행에서 어떻게 사용할 수 있을까요?

    stream-ref forces exactly as many tails as it needs to reach the requested index. Asking for index 9 constructs a finite prefix and returns 10, so this particular run terminates.

    삽입을 지역적인 변경으로 만들려면 앞 포인터뿐 아니라 뒤 포인터도 필요한 이유는 무엇일까요?

    Deletion does not rewrite the links. It advances front to its current cdr. After inserting a, b, and c and deleting once, front names the b pair while rear still names the c pair.

    상태가 매번 바뀌는 생성기가 어떻게 정확히 같은 수열을 반복할 수 있을까요?

    The recurrence is deterministic. Two separately constructed generators begin with private locations, but equal starting seeds make their value sequences equal. This small generator is useful for studying state and reproducibility, not for cryptography or security decisions.

    세 값 가운데 어떤 두 값이 먼저 와도 하나의 관계가 올바르게 반응하려면 무엇이 필요할까요?

    forget-value! succeeds only when the retractor is the stored informant. When the user forgets total, the adder retracts right because it supplied that derived value, but it cannot retract the independently supplied left value. Supplying a new right value then derives a new total through the same relation.

    시뮬레이션은 등록된 순서를 따르지 않고 다음 사건을 어떻게 고를까요?

    propagate removes the earliest event, applies its numeric change to signal, and records the resulting state beside that event time. The agenda models logical simulation time rather than waiting for a wall clock, and these examples do not model simultaneous-event physics beyond their explicit insertion rule.

    두 갱신이 어느 쪽도 끝나기 전에 같은 잔액을 읽으면 무엇이 사라질까요?

    The second schedule lets the deposit read and write before the withdrawal reads. The withdrawal therefore sees 110 and writes 90. These programs enumerate two already chosen finite schedules. They do not create threads or a serializer, and they do not establish the result of every possible interleaving.

    한 갱신이 상태를 읽은 뒤 다른 갱신이 끝나고 나서야 쓰는 일을 막으려면 무엇을 보호해야 할까요?

    make-serializer accepts a shared lock and returns a procedure wrapper. The wrapper acquires before calling the state-changing procedure and clears after receiving its result. In the shipped finite schedule, the serialized deposit finishes before the withdrawal reads balance, so both updates remain in the final value 90. The comparison program does not prove fairness, deadlock freedom, exception safety, or the outcome of every possible concurrent schedule.

    여러 지역 프로시저가 바깥 환경에 이름을 흘리지 않으면서 서로를 참조하려면 어떻게 해야 할까요?

    두 번째 프로그램은 정의를 인용된 데이터로 다루고 동시적인 지역 범위를 설명하는 scan-out 모양을 만듭니다. 먼저 모든 이름을 명시적인 unassigned 표시로 바인딩하고, set!으로 각 프로시저 값을 설치한 뒤, 남은 본문을 평가하는 형태입니다. 이번 실행은 변환을 데이터로 만들 뿐 생성된 식을 실행하거나 모든 Scheme 구현이 같은 중간 표현을 노출한다고 주장하지 않습니다.

    테이블에서 한 레코드를 갱신하거나 새로운 중첩 키 경로를 만들 때 어떤 연결을 바꿔야 할까요?

    두 키 테이블은 첫 번째 키 레코드의 cdr에 또 다른 연관 리스트를 저장합니다. 새로운 두 번째 키는 그 하위 테이블을 바꾸고, 새로운 첫 번째 키는 완전한 하위 테이블을 바깥 테이블에 연결합니다. arithmetic 값을 10에서 11로 바꾸는 일은 중복 레코드를 만들지 않고 가장 안쪽 기존 레코드를 갱신합니다. 이 예제는 #f를 조회 실패로 사용하므로 #f 자체를 저장하려면 더 풍부한 조회 규약이 필요합니다.