33검증 문제
현재 번호표 반환하기
1. 문제와 수정 범위
issueTicket은 호출 시점의 번호를 반환하고, 다음 호출에 쓸 번호를 1 늘려야 합니다.
레지스터 쓰기는 이미 올바릅니다. 그런데 반환 식이 갱신될 값을 한 번 더 계산하므로 처음
두 출력이 41, 42가 됩니다. 요구되는 출력은 40, 41입니다.
issueTicket 함수만 수정하세요. ActionValue#(UInt#(8)) 타입, actionvalue 블록,
레지스터 쓰기 한 번, mkTop은 그대로 둡니다.
2. starter 읽기와 실행 전 예측
issueTicket 안에서 next를 읽는 두 곳을 표시해 보세요. <=의 오른쪽 식은 다음 상태를
계산하고, return 식은 호출한 쪽이 받을 값을 계산합니다. 두 읽기는 모두 원자적 동작이
시작될 때의 상태를 봅니다. 예약된 쓰기는 동작이 끝난 뒤 다음 상태에서만 보입니다.
issueTwo의 <-도 확인해 보세요. <-는 ActionValue를 실행하고, 반환된 UInt#(8)
값을 ticket에 묶습니다. =를 쓰면 실행 결과가 아니라 동작 자체를 값으로 다루게
됩니다.
실행 전에 다음 표의 마지막 열을 직접 채워 보세요.
| 호출 | 시작할 때 next | 예약되는 next | starter의 반환값 |
|---|---|---|---|
| 첫 번째 | 40 | 41 | 반환 식으로 계산 |
| 두 번째 | 41 | 42 | 반환 식으로 계산 |
issued도 같은 규칙으로 바뀌므로 공개 실행은 정확히 두 번 호출합니다. 실행 결과에서
예측한 잘못된 출력 두 개를 확인해 보세요.
3. 최소 수정과 완료 조건
상태 갱신은 결함이 아니므로 쓰기를 지우거나 추가하지 마세요. 현재 호출이 받을 번호를 나타내도록 반환 식만 고칩니다.
- 초깃값 40에서 연속 호출의 반환값은 40, 41 순서입니다.
- 호출할 때마다
next가 정확히 한 번 늘어 41, 42가 됩니다. - 시작 값이
n이면 현재 호출은n을 반환하고, 다음 호출은 갱신된 상태를 읽습니다. - 함수는 상태 변경과 반환값을 함께 갖는
ActionValue로 남습니다.
수정한 뒤 실행에서 40 다음에 41이 출력되는지 확인하세요. 첫 반환값과 첫 번째로 반영된 다음 상태가 1 차이 나는 이유도 설명해 보세요. 출력만 40으로 고정하면 두 번째 호출과 다른 초깃값에서 계약이 깨집니다.
4. 단계형 힌트
힌트 1: 계약 나누기
반환값 계약과 레지스터 다음 상태 계약을 따로 적어 보세요. 요구 trace와 어긋나는 계약은 하나뿐입니다.
힌트 2: 읽기 시점
같은 원자적 동작 안에서는 쓰기를 먼저 적어도 뒤의 레지스터 읽기가 예약된 값을 보지 않습니다. 모든 읽기는 시작 상태를 기준으로 합니다. 문장 순서가 예약된 갱신을 드러내지는 않습니다.
힌트 3: 수정 위치
next에 대입하는 오른쪽 식은 이미 다음 호출에 필요한 상태를 계산합니다. return이 같은
계산을 되풀이해야 하는지 검토해 보세요.
5. 추가 실험
수정을 마친 뒤 별도 사본에서 증가량만 2로 바꿔 보세요. "현재 값을 반환하고 증가" 계약은 그대로 두고, 처음 두 반환값과 레지스터 상태를 실행 전에 계산해 보세요. 확인한 뒤에는 증가량을 1로 되돌리세요.