Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions devel/0074.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# [0074] 在 (scheme base) 中导出 call/cc,撰写对应的单元测试(包含文档)

## 1. 相关文档
- [dddd.md](dddd.md) - 任务文档模板

## 2. 任务相关的代码文件
- goldfish/scheme/base.scm
- tests/scheme/base/call-slash-cc-test.scm

## 3. 如何测试

### 3.1 确定性测试(单元测试)
```
bin/gf test tests/scheme/base/call-slash-cc-test.scm
```

### 3.2 非确定性测试(文档验证)
```
bin/gf doc "call/cc"
bin/gf doc "call-with-current-continuation"
```

## 4. 如何提交

提交前执行以下最少步骤:

```bash
bin/gf test --changed-since=main
```

## 5. 2026-05-31 在 (scheme base) 中导出 call/cc,撰写对应的单元测试(包含文档)
### 5.1 What
确认 `(scheme base)` 已导出 `call/cc`(`call-with-current-continuation` 的简写),并为其撰写包含文档和丰富测试用例的单元测试文件。

1. `goldfish/scheme/base.scm` 中已导出 `call/cc` 和 `call-with-current-continuation`
2. 更新 `tests/scheme/base/call-slash-cc-test.scm`,补充文档注释和更多测试用例

### 5.2 Why
`call/cc` 是 Scheme 语言最核心的特性之一(first-class continuations),需要:
- 确保其在 `(scheme base)` 中可见并可用
- 提供清晰的文档说明其语法、参数和语义
- 通过丰富的测试用例覆盖常见用法,包括基本返回、续延保存、多值返回、非局部退出等

### 5.3 How
1. **确认导出**:`goldfish/scheme/base.scm` 第 214-215 行已导出 `call-with-current-continuation` 和 `call/cc`
2. **撰写测试**:在 `tests/scheme/base/call-slash-cc-test.scm` 中编写包含以下内容的测试:
- 基本用法:过程正常返回
- 调用续延立即返回
- 保存续延供后续调用
- 从续延返回多个值
- 在嵌套表达式中使用
- 实现非局部退出(类似 break)
- 验证续延只影响调用栈、不恢复变量绑定
3. **验证文档**:运行 `bin/gf doc "call/cc"` 确认能正确显示文档和测试用例

### 5.4 测试用例说明

| 测试用例 | 说明 |
|---------|------|
| `(call/cc (lambda (k) 5))` | 基本用法,过程正常返回 |
| `(call/cc (lambda (k) (k 10) 20))` | 调用续延立即返回,忽略后续表达式 |
| 保存续延供后续调用 | 将续延保存到变量,之后再次调用 |
| 从续延返回多个值 | 与 `call-with-values` 配合使用 |
| 嵌套表达式中使用 | 验证续延返回值正确参与外层表达式求值 |
| 非局部退出 | 使用 `call/cc` 实现类似 break 的控制流 |
| 变量绑定不恢复 | 验证调用续延只重置调用栈,不恢复变量状态 |
147 changes: 145 additions & 2 deletions tests/scheme/base/call-slash-cc-test.scm
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,154 @@
;; 1. 续延可保存并稍后调用
;; 2. 调用续延会重置调用栈
;; 3. call/cc 是 call-with-current-continuation 的简写
;; 4. 续延可以作为第一类值传递和存储
;; 5. 调用续延时传入的值成为 call/cc 的返回值
;;
;; 示例
;; ----
;; 基本用法:过程正常返回
(check (call/cc (lambda (k) 5)) => 5)

;; 调用续延立即返回
(check (call/cc (lambda (k) (k 10) 20)) => 10)

;; 使用 call-with-current-continuation 完整名称
(check (call-with-current-continuation (lambda (k) (k 'done))) => 'done)
(let ((result (call/cc (lambda (k) (k 99) 1))))
(check result => 99)

;; 保存续延供后续调用
(let ((saved #f) (calls '()))
(set! calls (cons 'before calls))
(let ((result (call/cc (lambda (k) (set! saved k) 'first))))
(set! calls (cons result calls))
(when (eq? result 'first)
(saved 'second)
) ;when
) ;let
(check (reverse calls) => '(before first second))
) ;let

;; 从续延返回多个值
(check (call-with-values (lambda () (call/cc (lambda (k) (k 1 2 3))))
(lambda (a b c) (list a b c))
) ;call-with-values
=>
'(1 2 3)
) ;check

;; 在嵌套表达式中使用
(check (+ 1 (call/cc (lambda (k) (* 2 (k 3))))) => 4)

;; 实现非局部退出(类似 break)
(check (let ((sum 0))
(call/cc (lambda (return)
(for-each (lambda (x) (when (> x 5) (return sum)) (set! sum (+ sum x)))
'(1 2 3 4 5 6 7)
) ;for-each
sum
) ;lambda
) ;call/cc
) ;let
=>
15
) ;check

;; 续延只影响调用栈,不恢复变量绑定
(let ((counter 0) (saved #f))
(call/cc (lambda (k) (set! saved k)))
(set! counter (+ counter 1))
(when (< counter 3)
(saved 'retry)
) ;when
(check counter => 3)
) ;let

;; 用 call/cc 实现简单的生成器(yield 模式)
;; 每次调用生成器返回下一个值
(let ((make-generator (lambda (proc)
(let ((return #f) (resume #f))
(lambda ()
(call/cc (lambda (k)
(set! return k)
(if resume
(resume 'ok)
(begin
(proc (lambda (v) (call/cc (lambda (r) (set! resume r) (return v)))))
(return 'done)
) ;begin
) ;if
) ;lambda
) ;call/cc
) ;lambda
) ;let
) ;lambda
) ;make-generator
) ;
(let ((g (make-generator (lambda (yield) (yield 'a) (yield 'b) (yield 'c)))))
(check (g) => 'a)
(check (g) => 'b)
(check (g) => 'c)
(check (g) => 'done)
) ;let
) ;let

;; 用 call/cc 实现异常风格的抛出/捕获
;; throw 是一个接收续延的过程,当调用 throw 时,跳转到 catch 的上下文
(let ((catch-proc (lambda (thunk handler)
(call/cc (lambda (k) (let ((result (thunk k))) result)))
) ;lambda
) ;catch-proc
(throw-proc (lambda (k value) (k (list 'exception value))))
) ;
(check (catch-proc (lambda (throw-k) (+ 1 (throw-proc throw-k "error")))
(lambda (e) e)
) ;catch-proc
=>
'(exception "error")
) ;check
(check (catch-proc (lambda (throw-k) (+ 1 2)) (lambda (e) e)) => 3)
) ;let

;; 多次调用同一个续延,每次回到同一个"地点"
(let ((checkpoint #f) (results '()))
(let ((value (call/cc (lambda (k) (set! checkpoint k) 0))))
(set! results (cons value results))
(when (< value 3)
(checkpoint (+ value 1))
) ;when
) ;let
(check (reverse results) => '(0 1 2 3))
) ;let

;; 续延作为参数传递给其他函数

(define (escape-if-odd n return)
(when (odd? n)
(return 'odd)
) ;when
) ;define

(check (let ((n 4)) (call/cc (lambda (k) (escape-if-odd n k) 'even))) => 'even)

(check (let ((n 5)) (call/cc (lambda (k) (escape-if-odd n k) 'even))) => 'odd)

;; 续延对象是一个过程
(check (procedure? (call/cc (lambda (k) k))) => #t)

;; 调用续延后,续延点之后的代码不会执行
(let ((log '()))
(let ((result (call/cc (lambda (k)
(set! log (cons 'before log))
(k 'interrupted)
(set! log (cons 'after log))
'normal
) ;lambda
) ;call/cc
) ;result
) ;
(set! log (cons 'after-call log))
(check (reverse log) => '(before after-call))
(check result => 'interrupted)
) ;let
) ;let

(check-report)
33 changes: 33 additions & 0 deletions tests/scheme/base/call-with-current-continuation-test.scm
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
(import (liii check))
(import (scheme base))
(check-set-mode! 'report-failed)
;; call-with-current-continuation
;; 捕获当前续延,允许过程在之后恢复执行。
;;
;; call-with-current-continuation 与 call/cc 是同一个过程的不同名称,
;; 两者行为完全相同。详细测试用例和更多示例请参考 gf doc "call/cc"。
;;
;; 语法
;; ----
;; (call-with-current-continuation proc)
;;
;; 参数
;; ----
;; proc : procedure?
;; 接收一个续延对象作为参数的过程。
;;
;; 返回值
;; ------
;; 任意类型
;; proc 的返回值,或续延被调用时传入的值。
;;
;; 说明
;; ----
;; 1. call-with-current-continuation 是 call/cc 的完整名称
;; 2. 两者行为完全相同,只是名称长度不同
;; 3. call/cc 更为常用,尤其在需要频繁使用的场景

(check (call-with-current-continuation (lambda (k) 5)) => 5)
(check (call-with-current-continuation (lambda (k) (k 'done))) => 'done)

(check-report)
Loading