├── .github
└── workflows
│ └── gitbook-action.yml
├── .gitignore
├── .gitlab-ci.yml
├── 00.Terminology.md
├── 01.Introduction.md
├── 02.UsingChezScheme.md
├── 03.Debugging.md
├── 04.ForeignInterface.md
├── 05.BindingForms.md
├── 06.ControlStructures.md
├── 07.OperationsOnObjects.md
├── 08.NumericOperations.md
├── 09.InputOutputOperations.md
├── 10.LibrariesAndTop-levelPrograms.md
├── 11.SyntacticExtensionAndModules.md
├── 12.SystemOperations.md
├── 13.StorageManagement.md
├── 14.ExpressionEditor.md
├── 15.ThreadSystem.md
├── 16.CompatibilityFeatures.md
├── COPYING
├── LICENSE
├── README.md
├── SUMMARY.md
├── work.md
└── 字数统计方法示例.png
/.github/workflows/gitbook-action.yml:
--------------------------------------------------------------------------------
1 |
2 | name: 'Gitbook Action Build'
3 | on:
4 | push:
5 | branches:
6 | - master
7 | jobs:
8 | build:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Checkout action
12 | uses: actions/checkout@v2
13 | - name: Gitbook Action
14 | uses: guenchi/gitbook-action@v1.2.2
15 | with:
16 | token: ${{ secrets.gitbook_action_token }}
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.ss~
2 | *.ss#*
3 | .#*.ss
4 |
5 | *.scm~
6 | *.scm#*
7 | .#*.scm
8 |
9 | *.iml
10 | .idea/
11 |
--------------------------------------------------------------------------------
/.gitlab-ci.yml:
--------------------------------------------------------------------------------
1 | image: node:10
2 |
3 |
4 | cache:
5 | paths:
6 | - node_modules/
7 |
8 | before_script:
9 | - npm install gitbook-cli -g
10 | - gitbook fetch 3.2.3
11 | - gitbook install
12 |
13 | pages:
14 | stage: deploy
15 | script:
16 | - gitbook build . public
17 | artifacts:
18 | paths:
19 | - public
20 | expire_in: 1 week
21 | only:
22 | - master
23 |
--------------------------------------------------------------------------------
/00.Terminology.md:
--------------------------------------------------------------------------------
1 | # 术语
2 |
3 | ## A
4 |
5 | **Alias** 别名
6 |
7 | > `alias` 是一类定义,可以出现在其他定义可以出现的任何位置。它用于将绑定从一个标识符转移到另一个标识符。
8 |
9 | **Anonymous** 匿名的
10 |
11 | > 通常与**具名的([named](#named))**相对。
12 |
13 | **Annotation** 注解
14 |
15 | > 参考了 Java 语言中关于 annotation 的译法(见[Java注解](https://zh.wikipedia.org/wiki/Java%E6%B3%A8%E8%A7%A3))
16 |
17 |
18 | ## B
19 |
20 | **Binding** 绑定
21 |
22 | > 本应译为“约束”,但考虑到大多数人的习惯,故译为“绑定”,而且两者发音很像。
23 |
24 | **Bool/Boolean** 布尔 布尔的 布尔值
25 |
26 | > 继承自 R5RS 中译版的译法。
27 |
28 | **Body** 主体,语句体
29 |
30 | **Built-in** 内置(的)
31 |
32 | ## C
33 |
34 | **Continuation** 【不翻译】
35 |
36 | > 可以理解为 “the state of remaining in a particular position or condition”。
37 |
38 | **Compile-time** 编译期
39 |
40 | > 发生在“运行时(run-time)”之前的一个过程。考虑到“编译期函数”比“编译时函数”更加顺口,因而采用了编译*期*的译法。
41 |
42 | **Compile-time value** 编译期值
43 |
44 | **Compile-time property** 编译期属性
45 |
46 | **clause** 子句
47 |
48 | > 参考[wikipedia](https://zh.wikipedia.org/wiki/%E5%AD%90%E5%8F%A5) 对于clause的解释
49 |
50 | **condition** 状态
51 |
52 | >参考 [MIT Scheme 文档](https://groups.csail.mit.edu/mac/ftpdir/scheme-7.4/doc-html/scheme_17.html) ,一般是指异常状态
53 |
54 |
55 | ## D
56 |
57 | **Default** 默认的
58 |
59 | ## E
60 |
61 | **Enumeration** 枚举
62 |
63 | **Enumeration set** 枚举集
64 |
65 | **enter(ed)** 键入
66 |
67 | > 明确指出是由用户通过打字(type)敲入到计算机的,用来与 `import` 、`input` 或其它可能会被翻译为“输入”的术语相对。
68 |
69 | **Evaluate** 求值
70 |
71 | **Evaluator** 求值器
72 |
73 | **Expand** **Expansion** 展开
74 |
75 | > 宏的*展开*,区别于 `extend`(扩展),后者通常指*扩展*一个环境。
76 |
77 | **Expander** (宏的)展开器
78 |
79 | **Expand-time** 展开期
80 |
81 | ## F
82 |
83 | **Fender** 防护板
84 |
85 | > Chez Scheme extends `syntax-rules` to permit clause to include **fenders** just like those allowed within `syntax-case` clauses.
86 |
87 | **Field** 数据成员
88 |
89 | > Field, A data member of a class. Unless specified otherwise, a field is not static.
90 |
91 | **Fixnum** 定长数 固定数
92 |
93 | > A fixnum is an exact integer that is small enough to fit in a machine word.
94 | > 具体的讨论请参见:[term modification request: parameter fixnum](https://github.com/guenchi/CSUG/pull/16#issuecomment-453844719)
95 |
96 | **Form** 形式 特殊形式 语法形式
97 |
98 | > 按 [R6RS](http://www.r6rs.org/final/html/r6rs/r6rs-Z-H-4.html#node_sec_1.5) 的定义,form 是
99 | Scheme 程序中句法部分的一个通用名字。为区别一般的“形式”,故译为“语法形式”。
100 | > 详细讨论:[术语讨论:syntax、form、body](https://github.com/guenchi/CSUG/issues/22)
101 |
102 | ## G
103 |
104 | **Guardian** 守卫者
105 |
106 | > Guardians allow programs to protect objects from deallocation by the garbage
107 | collector and to determine when the objects would otherwise have been deallocated.
108 |
109 | ## I
110 |
111 | **Immutable** 不可变的
112 |
113 | **Inspect/Inspection** 审查
114 |
115 | > 不翻译为“检查”,是为了跟 **check** 有所区别;
116 |
117 | **Inspector** 审查器
118 |
119 | ## M
120 |
121 | **Mutually recursion/recursive** 互递归、互递归的
122 |
123 | > 函数 `f(x)` 的定义依赖于 `g(x)` 的定义,后者又依赖于 `f(x)` 的定义。这种依赖关系可以扩展到多个函数键的互递归关系。采用**互递归**而非**互相递归**是参考了“互引用”以及“互斥”等常见说法。
124 |
125 | ## N
126 |
127 | **Named** 具名的 命名的
128 |
129 | > 通常与**匿名的([anonymous](#anonymous))**相对。
130 |
131 | **Named module** 具名模块
132 |
133 | **Namespace** 名字空间
134 |
135 | > 一组通常由关键字名、变量名和模块名等标识符构成的数据聚合;一种习惯的译法是“命名空间”,但这更像是“named space”的译名。
136 |
137 | **Native** 原生的
138 |
139 | > 在计算领域,如果软件或数据格式对一个系统是 native 的,那就是说该系统可以以最小的计算开销或额外组件支持它 — 维基百科。
140 | > 考虑到大多数人的习惯,故译为“原生的”。
141 |
142 | ## P
143 |
144 | **Parameter** 参数对象 行为界限
145 |
146 | > + 所有的 Chez Scheme 系统都可以通过**参数对象(parameter)**来完成定制;
147 | > + 参数对象是一种过程,它封装了一个隐含的状态变量;
148 | > + 当无参调用该过程时,参数对象会返会所封装变量的值;
149 | > + 当以单个参数调用该过程时,参数对象将该状态变量修改为该实参的值;
150 | > + 如果传入的参数不当,该过程可能会抛出异常,也可能以某种方式过滤此参数。
151 | > 详细讨论:[term modification request: parameter fixnum](https://github.com/guenchi/CSUG/pull/16)
152 |
153 | **Procedure** 过程
154 |
155 | > 根据Lisp传统,函数一般被称为过程。对初学者来说可以简单的将其理解为函数,或方法。
156 |
157 | **Profile** 性能分析
158 |
159 | **Port** 端口
160 |
161 | > Scheme 中用来表示输入/输出设备的对象。继承自 R5RS 中译版的译法。
162 |
163 | ## R
164 |
165 | **R5RS** 《Scheme语言修订5报告》
166 | **R6RS** 《Scheme语言修订6报告》
167 |
168 | > 完整的名字应该是 Revised6 Report on the Algorithmic Language Scheme,即《算法语言Scheme修订6报告》,但为了简洁起见,推荐翻译为《Scheme语言修订6报告》。
169 | > 在 markdown 文件中应该以这样书写:`《Scheme语言修订6报告》`
170 |
171 | **Reader** 读取器
172 |
173 | **Record** 记录
174 |
175 | > 一种复合数据结构,可以认为是结构体的别称。
176 |
177 | **Record-type** 记录类型
178 |
179 | **Routine** 子程序 例程 过程
180 |
181 | + routine 一词,有“一套(相互联系的)动作”的含义(例如 a dance/skating routine)。
182 | + 在原文“the programmer can manipulate them directly via a set of **routines** for creating and accessing annotations”. 中,routine 一词更能体现出“有一组 set/get 过程用于操作 annotation”这种意味。
183 | + 从这个角度上来说,相比于简单地翻译为”子程序“或者”过程“,routine 一词自身的含义更加深刻。
184 | + 在不需要严谨区别 routine 含义的场合,推荐将其简单地翻译为“过程”。
185 |
186 | ## S
187 |
188 | **Scope** 作用域
189 |
190 | > 标识符的可见范围。
191 |
192 | **Self-recursion/recursive** 自递归、自递归的
193 |
194 | **Shadowed** 遮蔽
195 |
196 | ```scheme
197 | (let ((a 1))
198 | (let ((a 2))
199 | (display a)))
200 | ; => 2
201 | ```
202 |
203 | > 最内层 `a` 的绑定,**遮蔽**了最外层同名变量 `a` 的绑定。遮蔽一词,对语法关键字也适用。
204 |
205 | **Shared object** 动态链接库
206 |
207 | > Unix体系内的shared object (共享对象)统一翻译为更好理解的动态链接库,从而不与Windows的dynamic linking library加以区分。
208 |
209 | **Syntax** 语法 句法
210 |
211 | > 关于 syntax 和 grammar 的不同,可以参考[这篇文章](http://pediaa.com/difference-between-grammar-and-syntax/)。
212 | > 考虑到俗成的翻译约定,故依然采取“语法”的译法。
213 | > 详细讨论:[术语讨论:syntax、form、body](https://github.com/guenchi/CSUG/issues/22)
214 |
215 |
216 | ## T
217 |
218 | **Top-level** 顶层
219 |
220 | ** The Scheme Programming Language, 4th Edition ** 《Scheme 程序设计语言(第4版)》
221 |
222 | **Transformer** 转换器
223 |
224 | > 语法关键字对应的语义对象。它是一个句法域上的函数,即接受一个句法形式,返回一个新的句法形式。
225 |
226 | **trace** 跟踪
227 |
228 |
229 | ## W
230 |
231 | **wrapper** 包装(语法)、包装(过程)
232 |
233 | > 需要根据 wrapper 出现的上下文来判断。
--------------------------------------------------------------------------------
/01.Introduction.md:
--------------------------------------------------------------------------------
1 | # 简介
2 |
3 | 这本书描述了 _Chez Scheme_ 给 Scheme 修订6报告 (R6RS) 的扩展。它也
4 | 包含了对标准对精确对概括和 _Chez Scheme_ 的句法形式和过程,并给出了每个句法形式的句法
5 | 和每个过程所接受的参数数量和类型。R6RS 标准特性的细节可以在
6 | 《[Scheme 编程语言,第四版](http://www.scheme.com/tspl4/)》 (TSPL4) 或 [Scheme 修订6报告](https://r6rs.mrliu.org)
7 | 里找到。《Scheme 编程语言,第四版》也包含了大量的对 Scheme 语言的介绍和许多或短或长的例子。
8 |
9 | 这个文档的大部分也可以同样地应用于 _Petite Chez Scheme_,它可以完全兼容整个 _Chez Scheme_
10 | 系统,只不过使用了很快的解释器代替了 _Chez Scheme_ 的增量式原生代码编译器。
11 | 只要不涉及编译器调用,那么给 _Chez Scheme_ 写的程序可以原封不动地运行于 _Petite Chez Scheme_ 里。
12 | 事实上,_Petite Chez Scheme_ 和 _Chez Scheme_ 构建于相同的源代码,除了包含编译器代码,
13 | 其它的都一样。在2.8节,详细地讨论了这个差别的影响。
14 |
15 | 本章剩下的部分包括 _Chez Scheme_ 对 Scheme 句法的扩展 (第1.1节),本书用到的符号约定 (第1.2节),
16 | 用来定制系统的行为界限 (第1.3节),和在哪里可以找到更多关于 _Chez Scheme_ 的信息 (第1.4节)。
17 |
18 | 第二章描述了如何用 _Chez Scheme_ 开发程序,写脚本,交付应用,加上如何使编译器尽可能
19 | 生成更高效的代码。第三章描述了调试和对象检查工具。第四章记录了用于和不同进程或其它语言写的
20 | 代码如何交互的文档。第五章描述了绑定句法形式。第六章记录控制结构的文档。第七章记录了非数值
21 | 对象的操作的文档,而第八章记录了多种数值操作,包括高效的类型明确的操作。第九章描述了输入/输出
22 | 操作和一般性端口,一般性端口允许通过任意的输入输出语义来定义其它端口。第十章讨论了 R6RS 库
23 | 和顶层程序如何加载进 _Chez Scheme_,并可同时使用其众多的用于控制和追踪加载进程的特性。第
24 | 十一章描述了句法扩展和模块。第十二章描述了系统操作,例如与操作系统的交互和定制 _Chez Scheme_
25 | 的用户接口。第十三章描述了如何调用和控制存储管理系统和记录了守卫者和 weak pair 的文档。第十四章描述了
26 | _Chez Scheme_ 的表达式编辑器和如何定制它。第十五章记录了构成 _Chez Scheme_ 原生线程
27 | 系统接口的过程和句法形式。最后,第十六章描述了很多兼容特性。
28 |
29 | 这本书后面包含了参考书目,句法形式的总结,和索引。出现在句法形式总结中的页码和出现在索引中的
30 | 斜体页码指出了句法形式和过程在文中正式地被定义的位置。句法形式总结和索引包含了来自 TSPL4 的
31 | 条目,因此它们包含了 _Chez Scheme_ 整个的特性集合。TSPL4 条目的页码标记有前缀“t”。
32 |
33 | 这本书和 TSPL4 的在线版本和勘误可以在这里找到 [www.scheme.com](https://www.scheme.com)。
34 |
35 | _感谢_:Michael Adams, Mike Ashley, Carl Bruggeman, Bob Burger, Sam Daniel,
36 | George Davidson, Matthew Flatt, Aziz Ghuloum, Bob Hieb, Andy Keep,
37 | 和 Oscar Waddell 对开发 _Chez Scheme_ 作出的重大贡献。_Chez Scheme_ 的表达式编辑器
38 | 基于 C. David Boyer 从 1989 年到 1994 年开发的给 Scheme 的命令行编辑器。文件压缩用到
39 | 了 Jean-loup Gailly 和 Mark Adler 开发的 zlib 压缩库。实现 `list` 和 `vector`
40 | 排序的子程序基于 Olin Shiver 的机会主义合并排序算法和实现。Michael Lenaghan 为本书
41 | 的早期草稿提供了若干修正意见。这本书记录的很多特性都是现在的 _Chez Scheme_ 用户建议的,
42 | 并且用户的许多评论也提升了本书,在正文内容方面。欢迎提出更多可以提升 _Chez Scheme_ 和本书
43 | 的意见。
44 |
45 |
46 | ## 1.1 节 Chez Scheme 句法
47 |
48 | _Chez Scheme_ 在对象 (数据) 和句法形式层面都继承自 Scheme。在对象层面上,_Chez Scheme_
49 | 支持包含非标准字符的额外的符号表示,非十进制的浮点数和科学记法,显式长度的 `vector`,共享的
50 | 和循环的 `structure`,`record`,`box` 等等。接下来将描述这些扩展。句法形式层面的扩展
51 | 将会在贯穿全书和句法形式总结中描述,书后面也会出现。
52 |
53 | _Chez Scheme_ 以若干方式方法继承了标识符的语法。首先,组成标识符名字的字符序列可以以数字,句号,
54 | 加号和减号开头,只要这个序列不被解析为数字就可以。举个例子,`0abc`,`+++`,和 `..` 在
55 | _Chez Scheme_ 里均是有效的标识符。其次,单字符序列的 `{` 和 `}` 也是标识符。再次,包含任意
56 | 字符的可能会被用 `\` 或 `|` 转义后打印。`\` 用于转义单字符(除了 ‘x’,因为 `\x` 用于标记十六进制
57 | 标量值的开头),而 `|` 被用来转义一组跟着它到匹配到下个 `|` 的字符。举个例子,`\||\|` 是一个
58 | 名字包含两个字符,由字符 `|` 后跟字符 `\` 组成的标识符,`|hit me!|` 是一个名字中包含一个空白的标识符。
59 |
60 | 另外,`gensym` 会打印出用 `#{` 和 `}` 括号括起来的“美观”和“独特”的两种名字,例如,
61 | `#{g1426 e5g1c94g642dssw-a}`。也可以只用带前缀 `#:` 的“美观”名字打印,例如 `#:g1426`。
62 |
63 | 从 2 到 36 的任意基数可以用 `#nr` 来指定,这里的 `n` 是基数。大小写不重要,因此,使用 `#nR` 也是
64 | 一样的。数字值从 10 到 35 也可以用或小写或大写到字母字符来指定,就像十六进制数字那样。举个例子,
65 | `#36rZZ` 就是 `35 × 36 + 35` 或 1295。
66 |
67 | _Chez Scheme_ 也允许非十进制数字打印成浮点数或科学记法。举个例子,`#o1.4` 等于 `1.5`,
68 | `#b1e10` 等于 `4.0`。数字优先于指数符号,因此,`#x1e20` 就只简单的是四个数的十六进制数字,等于
69 | `7712`。
70 |
71 | 另外,附加了一些除标准知名字符如 `#\alarm`, `#\backspace`, `#\delete`, `#\esc`,
72 | `#\linefeed`, `#\newline`, `#\page`, `#\return`, `#\space`, 和 `#\tab` 之外,
73 | _Chez Scheme_ 还可以识别 `#\bel`, `#\ls`, `#\nel`, `#\nul`, `#\rubout`,
74 | 和 `#\vt` (或 `#\vtab`)。标量值小于 256 的字符也可以以八进制语法即由前缀 `#\ ` 后跟一个三个八进制
75 | 数字的序列打印。举个例子,`#\000` 等于 `#\nul`。
76 |
77 | _Chez Scheme_ 的 `fxvector`,或者固定数 `vector`,将以类似 `vector` 但用前缀 `#vfx(` 代替了
78 | `#(` 打印。`vector`,`bytevector` 和 `fxvector`也可以以显式长度前缀打印,并且当指定了显式长度前缀,
79 | 尾部重复元素也将被省略。举个例子,`#(a b c)` 也可以打印成 `#3(a b c)`,长度 100 元素全是零的 `vector`
80 | 可以打印成 `#100(0)`。
81 |
82 | _Chez Scheme_ 的 `box` 将以 `#&` 前缀打印,例如,`#&17` 是一个包含整数 `17` 的 `box`。
83 |
84 | `record` 将以 `#[类型名 数据成员 ...]` 的语法打印,这里的符号 `类型名` 是 `record` 类型的名字,
85 | `field ...` 是 `record` 的数据成员内容的打印表示。
86 |
87 | 共享结构可以打印为前缀 `#n=` 的图标记,循环结构可以打印为前缀 `#n#` 的引用。`#n=` 用来标记东西作为
88 | 输入,`#n#` 用来引用标记为 `n` 的东西。举个例子,`'(#1=(a) . #1#)` 是一个 pair,其 `car` 和
89 | `cdr` 乃同一个列表,`#0=(a . #0#)` 是一个循环列表,也就是说,它的 `cdr` 是它自己。
90 |
91 | `$primitive` 形式也可以像 `quote` 一样可以简写,用前缀 `#%`。举个例子,`#%car` 等价于
92 | `($primitive car)`,`#2%car` 等价于 `($primitive 2 car)`,`#3%car` 等价于 `($primitive 3 car)`。
93 |
94 | _Chez Scheme_ 的文件结束对象打印为 `#!eof`。如果文件结束对象出现在正被加载的文件中的任何数据对象之外,
95 | `load` 将以文件真正结束对待,即在这个点停止加载。因此可以通过在文件中间插入 `#!eof` 来方便地追踪加载时错误。
96 |
97 | Weak pair 中损坏的指针表示为 _broken weak pair_ 对象,打印为 `#!bwp`。
98 |
99 | 作为对标准分割符(空白,开闭圆括号,开闭方括号,双引号,分号和 `#`)的补充,_Chez Scheme_ 也可以将开闭花括号,
100 | 单引号,反引号,逗号视作分割符。
101 |
102 | 在输入流中看到 `#!r6rs` 注释指令后,上面描述的 _Chez Scheme_ 的词法扩展将失效,除非在之前看到 `#!chezscheme`
103 | 指令。每个通过 `import` 隐式加载的库和每个通过 `--program` 命令行选项,`scheme-script` 命令,或 `load-program`
104 | 过程加载的 RNRS 顶级程序都将会以它以隐式的 `#!r6rs` 注释指令开头来对待。
105 |
106 | 按照修订报告6的要求,符号和字符名字的大小写通常都是重要的。在相同的输入流里,就像被 `string-foldcase`处理过
107 | 一样,`#!fold-case` 注释指令后的名字都是小写的,除非看到 `#!no-fold-case`。没有指令的时候,且 `case-sensitive`
108 | 行为界限被设置成 `#f` 时,名字也都是小写的。
109 |
110 | 打印机被 `write`, `put-datum`, `pretty-print`, 和 `format ~s` 选项调用总是用标准语法打印修订报告6
111 | 的标准对象,除非通过设置一个打印行为界限来请求不同的行为。举个例子,要使用上面描述的十六进制标量值转义序列来打印
112 | _Chez Scheme_ 扩展标识符语法的符号, 除非设置 `print-extended-identifiers` 行为界限为真。类似的,除非设置
113 | `print-vector-length` 行为界限为真,否则不会打印显式长度或抑制尾部重复元素。
114 |
115 | ## 1.2 记号惯例
116 |
117 | 本书跟《Scheme 编程语言,第四版》使用了几近相同的记号惯例。下面重复了这些惯例和 一些 _Chez Scheme_ 特有的标注。
118 |
119 | 当说一个过程或句法形式产生的值是 _未确定的_ ,那么这个过程或形式也许返回了任意数量的值,每个都是任意 Scheme 对象。
120 | 当结果是非确定的时,_Chez Scheme_ 常会返回仅有一个,独一无二的 _空_ 对象 (参见 `void`);然而,尤其当你的程序要移植到
121 | 其它的 Scheme 实现时,应该避免算上这个行为。_Chez Scheme_ 的 `waiter` (读-求值-打印循环,REPL) 会抑制打印空对象。
122 |
123 | 这本书用“必须”和“应该”这样的词来描述程序的需要,例如调用 `vector-ref` 时需要提供一个小于 `vector` 长度的索引。如果
124 | 用了“必须”一词,那意味着这个需要是该实现强制的,即会抛出异常,常使用 condition 类型 `&assertion`。如果用了
125 | “应该”一词,那么异常可能会也可能不会被抛出。如果未抛出,那么该程序的行为则是未确定的。短语“句法违规”用来描述程序
126 | 畸形时的情况。句法违规在程序执行前会被发现。当发现句法违规时,程序将抛出 `&syntax` 类型的异常,并停止执行。
127 |
128 | Scheme 对象显示为 `打字机` 字体,就像它们是在键盘上按出来的一样。这些对象包括标识符,常量对象,括起来的 Scheme 表达式,和
129 | 整个程序。斜体字体用来衬托句法形式和过程描述中的句法变量。当斜体字首次出现时,也用斜体字来作衬。标识符的首字母通常不会大写,
130 | 即使出现在句首也不会大写。这对写作斜体字的句法变量来说也是一样的。
131 |
132 | 在描述句法形式和过程时,是用一个模式来展示句法形式和过程应用的。句法关键字和过程名是以打字机字体给出的,圆括号也是。句法或
133 | 参数剩余的片段,用来暗示句法形式或过程要求的表达式或参数类型的名字都用斜体展示。
134 |
135 | ## 1.3 行为界限
136 |
137 | 所有 _Chez Scheme_ 的系统定制都是通过 _行为界限_ 来完成的。行为界限是一个封装了隐藏的状态变量的过程。当无参调用时将返回
138 | 封装的变量的值。当以一个参数调用时,行为界限将改变这个变量的值为参数的值。当它的参数不合适时,行为界限可能会抛出异常,
139 | 或者可能以某种方式过滤这个参数。
140 |
141 | 新的行为界限可能被运行于 _Chez Scheme_ 的程序所创建和使用。使用行为界限而不是全局变量来让程序定制具体行为有两个原因:
142 | 首先,非故意的重定义一个定制变量会导致不可预料的问题,然而,非故意的重定义行为界限只简单地使行为界限不可触及。举个例子,
143 | 一个程序因为自己的目的定义了 `*print-level*` 并在 _Chez Scheme_ 里提早声明,这将导致在打印 Scheme 对象时
144 | 一些不可预料的影响,然而,一个程序因为自己的目的定义了 `print-level` 行为界限,只是简单地损失了改变打印机行为的能力。
145 | 当然,一个程序调用这个 `print-level` 也仍然会以未计划的方式影响这个系统,发生事故,但这种事更少发生,而且只会出现在
146 | 一个不正确的程序之中。
147 |
148 | 其次,给行为界限无效的值在“赋值”时会被立即发现并拒收,而不是在第一次使用时的点上出现,那样找回并恢复旧值就太晚了。举个例子,
149 | 给 `*print-level*` 赋值为 -1 将不会被捕获直到首次调用 `write` 或 `pretty-print`,然而当尝试将 -1 赋值给行为界限
150 | `print-level` 时,即 `(print-level -1)`,就会在真的发生改动之前,立即出现一个错误信号。
151 |
152 | 内置的系统行为界限在贯穿这本书的各个章节都会被描述,并在这本书后面形式总结中的其它句法形式和过程中被列出。在线程版的 _Chez Scheme_
153 | 中,标记为“线程行为界限”的行为界限,各线程有各自的值,然而标记为“全局行为界限”的行为界限的值将被所有的线程共享。非线程
154 | 版本的 _Chez Scheme_ 不能分辨线程和全局行为界限的差别。参见第 12.13 节和第 15.6 节来获取创建和操作行为界限的更多信息。
155 |
156 | ## 1.4 更多信息
157 |
158 | 下面列出了记录了很多 _Chez Scheme_ 和其实现的特性文章和技术报告:
159 |
160 | - 句法抽象 [14,8,17],
161 | - 模块 [32],
162 | - 库 [21],
163 | - 存储管理 [12,13],
164 | - 线程 [10],
165 | - 多返回值 [2],
166 | - 可选参数 [16],
167 | - continuation [7,25,3],
168 | - eq? 哈希表 [20],
169 | - 内部定义, letrec, 和 letrec* [33,22],
170 | - equal? [1],
171 | - 引擎 [15],
172 | - 浮点数打印 [4],
173 | - 代码生成 [18],
174 | - 寄存器分配 [6],
175 | - 过程内联 [31],
176 | - 性能分析 [5],和
177 | - 实现的历史 [9]。
178 |
179 | 这些出版物的摘要和电子版本可以在 [http://www.cs.indiana.edu/chezscheme/pubs/](http://www.cs.indiana.edu/chezscheme/pubs/)
180 | 获取。
181 |
182 | > 译注:上面的方括号内的数字索引对应的位置可以在[这里](http://cisco.github.io/ChezScheme/csug9.5/bibliography.html)找到相应的信息。
183 |
184 | ---
185 |
186 | [Chez Scheme 第 9 版用户手册](http://cisco.github.io/ChezScheme/csug9.5/index.html)
187 |
188 | 思科系统公司版权所有 © 2018
189 |
190 | 按 [ Apache License Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) 授权
191 | ([完整的版权公告](http://cisco.github.io/ChezScheme/csug9.5/canned/copyright.html))
192 |
193 | 为 Chez Scheme 9.5.1 版本修改于 2018 年十月
194 |
195 | [关于本书](http://cisco.github.io/ChezScheme/csug9.5/canned/about.html)
196 |
--------------------------------------------------------------------------------
/02.UsingChezScheme.md:
--------------------------------------------------------------------------------
1 | # 使用 Chez Scheme
2 |
3 | _Chez Scheme_ 常常以交互的方式来支持程序开发和排除错误,它也可以使用非交互组件来创建
4 | 独立应用。本章描述了 _Chez Scheme_ 一贯如何使用,更一般地讲,是如何充分利用系统。
5 | 2.1,2.2 和 2.3 节描述了如何交互地使用 _Chez Scheme_。2.4 节讨论了在 _Chez Scheme_
6 | 中如何使用库和 RNRS 顶级程序。2.5 节包含了支持写和运行编译的脚本和编译的 RNRS 顶级程序。
7 | 2.6 节描述了如何通过组织和编译应用来尽可能充分利用编译器,获得更高效的代码。2.7 节描述了如何定制启动
8 | 过程,例如,改变或排除命令行选项,预加载 Scheme 或外部代码,或将 _Chez Scheme_ 作为
9 | 另一个程序的低级别程序来运行。2.8 节描述了如何用 _Petite Chez Scheme_ 作为运行时来支持
10 | _Chez Scheme_ 构建的应用。最后,2.9 节包含了调用 _Chez Scheme_ 时使用的命令行参数。
11 |
12 | ## 2.1 节 和 Chez Scheme 交互
13 |
14 | 一个最简单和高效的书写和测试 Scheme 程序的方法是组合使用文本编辑器像 `vi` 或 `emacs` 和在运行了
15 | _Chez Scheme_ 的 shell 窗口里交互地测试。如果 _Chez Scheme_ 按默认选项安装,在 shell 提示符
16 | 中键入 `scheme` 命令来启动一个交互的 Scheme 会话。在 _Petite Chez Scheme_ 中,`petite` 命令
17 | 做了同样的事情。在键入这个命令后,应该会看到简短的问候后跟只有一个尖括号的一行,像这样:
18 |
19 | ```scheme
20 | Chez Scheme Version 9.5.1
21 | Copyright 1984-2017 Cisco Systems, Inc.
22 |
23 | >
24 | ```
25 |
26 | 你应该也看见了位于尖括号右侧的光标。这个尖括号是系统的 REPL 发出的,它代表了 “读(R)求值(E)打印(P)循环(L)”,
27 | 如此称谓是因为它会读,求值并打印表达式,然后循环回下一轮读,求值,打印,诸如此类。(在 _Chez Scheme_ 里,
28 | REPL 也被称为 waiter)
29 |
30 | 为回应提示符,你可以键入任何 Scheme 表达式。如果这个表达式是符合语法规则的,REPL 会运行表达式并打印其值。
31 | 这里有一些例子:
32 |
33 | ```scheme
34 | > 3
35 | 3
36 | > (+ 3 4)
37 | 7
38 | > (cons 'a '(b c d))
39 | (a b c d)
40 | ```
41 |
42 | REPL 使用的 reader 比普通的 reader 更复杂。事实上,它是一个成熟的,像一个常规文本编辑器但一次只能编辑一个表达式
43 | 的“表达式编辑器”(简写为 “expeditor”)。一件你马上可能注意到的事是这个系统会自动缩进一个表达式的第二及后续的行。
44 | 举个例子,比如说我们想定义 `fact`,一个实现了阶乘函数的过程。如果我们键入 `(define fact` 后跟回车键,光标应该
45 | 位于 `define` 的第一个 `e` 下面,至此如果我们接着键入 `(lambda (x)` 我们应该看到:
46 |
47 | ```scheme
48 | > (define fact
49 | (lambda (x)
50 | ```
51 |
52 | expeditor 也允许我们在表达式内四处移动(即使跨多行),编辑表达式来修正错误。接着键入:
53 |
54 | ```scheme
55 | > (define fact
56 | (lambda (x)
57 | (if (= n 0)
58 | 0
59 | (* n (fact
60 | ```
61 |
62 | 我们也许注意到,过程的参数命名为 `x`,但已被我们引用为了 `n`。我们可以用箭头键移回第二行,用退格键删除这个惹麻烦的 `x`,
63 | 替换它为 `n`。
64 |
65 | ```scheme
66 | > (define fact
67 | (lambda (n)
68 | (if (= n 0)
69 | 0
70 | (* n (fact
71 | ```
72 |
73 | 我们这下可以用箭头键回到表达式的结尾,并完成定义。
74 |
75 | ```scheme
76 | > (define fact
77 | (lambda (n)
78 | (if (= n 0)
79 | 0
80 | (* n (fact (- n 1))))))
81 | ```
82 |
83 | 现在我们有了一个圆括号平衡的完全形式,如果我们在刚在最后的圆括号之后的光标处敲下回车键,expeditor 将把它送给求值器。
84 | 我们得到另一个右尖括号提示符时,我们就知道它已被接受。
85 |
86 | 现在我们可以通过输入一些东西来测试我们的定义了,说 `(fact 6)` 来获得提示符的响应:
87 |
88 | ```scheme
89 | > (fact 6)
90 | 0
91 | ```
92 |
93 | 打印的值不是我们所希望的,因为是 6 而实际上是 720。问题显然出在基础情况的返回值是 0 而不是 1。幸运的是,我们并不需要
94 | 为了修正错误而重新键入这个定义。替代地,我们可以使用 expeditor 的历史机制取回早前的定义。上箭头键可以贯穿历史向后移动。
95 | 这种情况下,第一次按上箭头键会取回 `(fact 6)`,第二次会取回 `fact` 的定义。
96 |
97 | 当我们向后移动历史时,表达式编辑器只给我们显示第一行,因此在两次向上箭头后,这是我们看见定义的全部:
98 |
99 | ```scheme
100 | > (define fact
101 | ```
102 |
103 | 我们可以通过键入 `^L` (control L,也就是 control 和 L 键一起按)强制 expeditor 显示整个表达式:
104 |
105 | ```scheme
106 | > (define fact
107 | (lambda (n)
108 | (if (= n 0)
109 | 0
110 | (* n (fact (- n 1))))))
111 | ```
112 |
113 | 现在我们可以移动至第四行并将 0 改成 1。
114 |
115 | ```scheme
116 | > (define fact
117 | (lambda (n)
118 | (if (= n 0)
119 | 1
120 | (* n (fact (- n 1))))))
121 | ```
122 |
123 | 我们现在已经准备好输入这个正确的定义了。如果光标在第四行我们按下回车键,然而它只会在旧的第四行和第五行之间开一个新行。
124 | 这在其它状况下可能是有用的,但不是现在。当然,我们可以通过用箭头键移动到表达式的结尾来变通,但更简单的方式是键入 `^J`
125 | ,这将强制表达式立即被输入而无论光标在何处。
126 |
127 |
--------------------------------------------------------------------------------
/03.Debugging.md:
--------------------------------------------------------------------------------
1 | # 第三章 调试
2 |
3 | `Chez Scheme` 有几个用于调试的功能。除了对经过完全类型检查过的运行中的代码提供错误信息之外,`Chez Scheme` 也支持对过程调用进行跟踪,中断任意计算,重定义异常和中断处理器,检查任意对象,包括异常的continuation 和中断。
4 |
5 | `Scheme` 或 `Chez Scheme` 的新手程序员,甚至是有经验的 Scheme 程序员都可以参考“How to Debug Chez Scheme Programs” 这个教程。在 可以找到 HTML 和 PDF 版本的教程。(译者注:此链接在翻译时无法打开 2019/2/1)
6 |
7 | ## 3.1 跟踪
8 |
9 | 跟踪是调试 Scheme 程序最有用的手段之一。 `Chez Scheme` 可以跟踪任何内建和用户定义的过程。跟踪库会将每一个调用过程的参数和返回值打印出来,通过紧凑的缩进机制来显示嵌套调用的深度。打印非尾递归调用的时候会增加缩进而尾递归调用不会,可以以此区分是尾递归调用还是非尾递归调用。对于嵌套大于等于 10 的调用,会在缩进处有一个括号,其中包括一个数字用于说明嵌套的深度。
10 |
11 | 这一节会涵盖跟踪过程和控制跟踪输出的机制。
12 |
13 | - 语法: (trace-lambda name formals body1 body2 …)
14 | - 返回: 一个被跟踪的过程
15 | - 所属库: (chezscheme)
16 |
17 | 一个 `trace-lambda` 表达式等价于一个 `lambda` 表达式加上一样的语法形式以及程序体,除了无论何时该过程被调用的时候都会向跟踪端口输出跟踪信息。这里使用 `name` 来命名该过程。跟踪信息会显示过程的参数以及过程的返回值,嵌套的调用会以缩进显示。
18 |
19 | 下面被跟踪的过程 `half` 返回整型参数除以 2 的商。
20 |
21 | ```scheme
22 | (define half
23 | (trace-lambda half (x)
24 | (cond
25 | [(zero? x) 0]
26 | [(odd? x) (half (- x 1))]
27 | [(even? x) (+ (half (- x 1)) 1)])))
28 | ```
29 |
30 | 对于 `(half 5)` 这个返回值是 2 的调用跟踪如下
31 |
32 | ```scheme
33 | |(half 5)
34 | |(half 4)
35 | | (half 3)
36 | | (half 2)
37 | | |(half 1)
38 | | |(half 0)
39 | | |0
40 | | 1
41 | |2
42 | ```
43 |
44 | 以上的例子展示了跟踪库对于尾递归和非尾递归调用的不同处理方式。因为 `half` 在参数是奇数的时候会调用自身,所以 `(half 5)` 与 `(half 4)` 的缩进层级是一样的,另外,因为 `(half 5)` 和 `(half 4)`的返回值是一样的所以这两个调用的返回值只显示一个。
45 |
46 | - 语法: (trace-case-lambda name clause ...)
47 | - 返回值: 一个被跟踪的过程
48 | - 所属库: (chezscheme)
49 |
50 | `trace-case-lambda` 表达式等价于 `case-lambda` 加上同样的子句,除了在调用该过程的时候会向跟踪输出端口输出跟踪信息,这里同样使用 `name` 来标记该过程。跟踪信息会展示此过程对应的参数以及该过程的返回值,同样使用缩进来表示嵌套调用。
51 |
52 | - 语法: (trace-let name ((var expr) …) body1 body2 …)
53 | - 返回值: 程序体 body1 body2 ... 的值
54 | - 所属库: (chezscheme)
55 |
56 | `trace-let` 表达式等价与有同样名字、绑定和程序体的 `let` 表达式除了在进入或重入(通过对绑定在 name 上的过程的调用)`trace-let` 表达式的时候会向跟踪端口输出跟踪信息。
57 |
58 | 一个以下形式 `trace-let` 的表达式
59 |
60 | ```scheme
61 | (trace-let name ([var expr] ...)
62 | body1
63 | body2
64 | ...)
65 | ```
66 |
67 | 可以使用 `trace-lambda` 重写为以下形式:
68 |
69 | ```scheme
70 | ((letrec ([name
71 | (trace-lambda name (var ...)
72 | body1 boddy2)])
73 | name)
74 | expr ...)
75 | ```
76 |
77 | `trace-let` 可以用来跟踪 let 表达式以及具名的 let 表达式,只要这个名字在 let 表达式的程序体中不是自由变量。有时候也可以将 `trace-let` 插入到程序中用于展示当前跟踪程序任意表达式的值。
78 | 例如下面这个版本的 `half`
79 |
80 | ```scheme
81 | (define half
82 | (trace-lambda half (x)
83 | (cond
84 | [(zero? x) 0]
85 | [(odd? x) (half (trace-let decr-value () (- x 1)))]
86 | [(even? x) (+ (half (- x 1)) 1)])))
87 | ```
88 |
89 | 调用 `(half 5)` 会有如下输出
90 |
91 | ```scheme
92 | |(half 5)
93 | | (decr-value)
94 | | 4
95 | |(half 4)
96 | | (half 3)
97 | | |(decr-value)
98 | | |2
99 | | (half 2)
100 | | |(half 1)
101 | | | (decr-value)
102 | | | 0
103 | | |(half 0)
104 | | 1
105 | |2
106 | ```
107 |
108 | - 语法: (trace-do ((var init update) …) (test result …) expr …)
109 | - 返回值: 最后一个 result 的值
110 | - 所属库: (chezscheme)
111 |
112 | `trace-do` 等价与拥有子语句的的 do 表达式,除了向跟踪端口输出跟踪信息。跟踪信息包括 `var` …的值、每次迭代以及循环结束后的最终返回值。
113 |
114 | 例如,表达式:
115 |
116 | ```scheme
117 | (trace-do ([old '(a b c) (cdr old)]
118 | [new '() (cons (car old) new)])
119 | ((null? old) new))
120 | ```
121 |
122 | 会有如下的跟踪输出
123 |
124 | ```scheme
125 | |(do (a b c) ())
126 | |(do (b c) (a))
127 | |(do (c) (b a))
128 | |(do () (c b a))
129 | |(c b a)
130 | ```
131 |
132 | 并且返回 `(c b a)`
133 |
134 | - 语法: (trace var1 var2 …)
135 | - 返回值: 包含 var1 var2 … 的 list
136 | - 语法: (trace)
137 | - 返回值: 当前被跟踪的所有顶层变量组成的 list
138 | - 所属库: (chezscheme)
139 |
140 | 第一种形式中, `trace` 会将顶层变量 var1,var2 …(必须是过程),重新赋值为等价的用`trace-lambda`定义的过程用于展示跟踪信息。
141 |
142 | `trace` 会将被跟踪的过程中的每一个变量原来的值封装。他可以大致如下定义(实际的版本会记录并返回被跟踪变量的值)
143 |
144 | ```scheme
145 | (define-syntax trace
146 | (syntax-rules ()
147 | [(_ var ...)
148 | (begin
149 | (set-top-level-value! 'var
150 | (let ([p (top-level-value 'var)])
151 | (trace-lambda var args (apply p args))))
152 | ...)]))
153 | ```
154 |
155 | 以这种方式跟踪的过程的跟踪可以通过 `untrace` (见下文)禁用,将相应变量赋值给不同的未跟踪值,或者随后对同一变量使用跟踪。因为被跟踪的是值而不是绑定,但是一个在跟踪被禁用前已经被跟踪的值,在跟踪被禁止后还是存在的话依然会被跟踪。
156 |
157 | `trace` 不带任何子表达式会返回所有当前被跟踪的变量的 list 。如果一个变量被跟踪并且随后没有被禁用跟踪或者被赋予另一个值那就是当前被跟踪的变量。
158 |
159 | 下面这段例举了如何在 REPL 中使用 `trace`
160 |
161 | ```scheme
162 | > (define half
163 | (lambda (x)
164 | (cond
165 | [(zero? x) 0]
166 | [(odd? x) (half (- x 1))]
167 | [(even? x) (+ (half (- x 1)) 1)])))
168 | > (half 5)
169 | 2
170 | > (trace half)
171 | (half)
172 | > (half 5)
173 | |(half 5)
174 | |(half 4)
175 | | (half 3)
176 | | (half 2)
177 | | |(half 1)
178 | | |(half 0)
179 | | |0
180 | | 1
181 | |2
182 | 2
183 | > (define traced-half half)
184 | > (untrace half)
185 | (half)
186 | > (half 2)
187 | 1
188 | > (traced-half 2)
189 | |(half 2)
190 | |1
191 | 1
192 | ```
193 |
194 | - 语法: (untrace var1 var2 …)
195 | - 语法: (untrace)
196 | - 返回: 一个未被跟踪的变量的 list
197 | - 所属库: (chezscheme)
198 |
199 | `untrace` 将 var1、var2 … 这些当前被跟踪的顶层变量恢复为原来的( 被`trace` 之前的)值,相当于禁止对这些变量的值的跟踪。如果 var1、var2 … 中有未被跟踪的值那么它将会被忽略。如果调用 `untrace` 的时候不加任何参数,那么所有当前被跟踪的变量都会被复原。
200 |
201 | 以下这段代码演示了如何在 REPL 中使用`trace` 和 `untrace` 调试一段不正确的过程定义。
202 |
203 | ```scheme
204 | > (define square-minus-one
205 | (lambda (x)
206 | (- (* x x) 2)))
207 | > (square-minus-one 3)
208 | 7
209 | > (trace square-minus-one * -)
210 | (square-minus-one * -)
211 | > (square-minus-one 3)
212 | |(square-minus-one 3)
213 | | (* 3 3)
214 | | 9
215 | |(- 9 2)
216 | |7
217 | 7
218 | > (define square-minus-one
219 | (lambda (x)
220 | (- (* x x) 1))) ; change the 2 to 1
221 | > (trace)
222 | (- *)
223 | > (square-minus-one 3)
224 | |(* 3 3)
225 | |9
226 | |(- 9 1)
227 | |8
228 | 8
229 | > (untrace square-minus-one)
230 | ()
231 | > (untrace * -)
232 | (- *)
233 | > (square-minus-one 3)
234 | 8
235 | ```
236 |
237 | 第一次对于 `square-minus-one` 的调用说明其中有错误,第二次(跟踪后)表明了是在那一步中出现了错误,第三次调用说明已经成功地修复了这个问题,第四次调用说明 `untrace` 没有消除这次修复。
238 |
239 | - 线程参数: trace-output-port
240 | - 所属库: (chezscheme)
241 |
242 | `trace-output-port` 这个参数决定了跟踪的信息会被发送给哪个输出端口。无参调用时, `trace-output-port` 会返回当前跟踪的输出端口。当给定一个参数的时候(必须是文本输出端口), `trace-output-port` 会改变当前的跟踪输出端口。
243 |
244 | - 线程参数: trace-print
245 | - 所属库: (chezscheme)
246 |
247 | `trace-print` 的值必须是一个接受两个参数的过程,一个对象以及一个输出端口。跟踪库会使用 `trace-print` 的值来打印被跟踪的过程的参数和返回值。 `trace-print` 默认是 `pretty-print`
248 |
249 | 跟踪库会在调用 `trace-print` 之前,为当前的嵌套层级将 `pretty-initial-indent` 的值设置为一个合适的值
250 | ,以此保证多行输出可以正确地被缩进。
251 |
252 | - 语法: (trace-define var expr)
253 | - 语法: (trace-define (var . idspec) body1 body2 …)
254 | - 返回值: 不确定
255 | - 所属库: (chezscheme)
256 |
257 | `trace-define` 是一个方便的简记用来定义一个变量,然后将其绑定到一个被跟踪的同样名字的过程。
258 | 前者等价于
259 |
260 | ```scheme
261 | (define var
262 | (let ([x expr])
263 | (trace-lambda var args
264 | (apply x args))))
265 | ```
266 |
267 | 后者等价于
268 |
269 | ```scheme
270 | (define var
271 | (trace-lambda var idspec
272 | body1 body2 ...))
273 | ```
274 |
275 | 对于前者, `expr` 必须等价于一个过程
276 |
277 | ```scheme
278 | > (let ()
279 | (trace-define plus
280 | (lambda (x y)
281 | (+ x y)))
282 | (list (plus 3 4) (+ 5 6)))
283 | |(plus 3 4)
284 | |7
285 | (7 11)
286 | ```
287 |
288 | - 语法: (trace-define-syntax keyword expr)
289 | - 返回值: 不确定
290 | - 所属库: (chezscheme)
291 |
292 | `trace-define-syntax` 会跟踪 `expr` 转换器中值的输入和输出,但是会除去展开时用于维护词法作用域的上下文信息。
293 |
294 | ```scheme
295 | > (trace-define-syntax let*
296 | (syntax-rules ()
297 | [(_ () b1 b2 ...)
298 | (let () b1 b2 ...)]
299 | [(_ ((x e) m ...) b1 b2 ...)
300 | (let ((x e))
301 | (let* (m ...) b1 b2 ...))]))
302 | > (let* ([x 3] [y (+ x x)]) (list x y))
303 | |(let* (let* [(x 3) (y (+ x x))] [list x y]))
304 | |(let ([x 3]) (let* ([y (+ x x)]) (list x y)))
305 | |(let* (let* [(y (+ x x))] [list x y]))
306 | |(let ([y (+ x x)]) (let* () (list x y)))
307 | |(let* (let* () [list x y]))
308 | |(let () (list x y))
309 | (3 6)
310 | ```
311 |
312 | 去除了上下文信息之后,展示的语句更加易读但是缺失了精确性,因为同样名字的不同标识符难以区别了。
313 | 例如:
314 |
315 | ```scheme
316 | > (let ([x 0])
317 | (trace-define-syntax a
318 | (syntax-rules ()
319 | [(_ y) (eq? x y)]))
320 | (let ([x 1])
321 | (a x)))
322 | |(a (a x))
323 | |(eq? x x)
324 | #f
325 | ```
326 |
327 |
328 | ## 3.2 交互式调试器
329 |
330 | 在一个异常被默认的异常处理器处理之后调用 `debug` 就可以进入交互式调试器。对于严重或者非警告的状态(异常状态),如果 `debug-on-exception` 参数是真,也可以直接从默认的异常处理器进入。
331 |
332 | 在调试器内,命令 “?” 会列出调试命令选项。包括以下用途的命令:
333 |
334 | - 检查 raise continuation
335 | - 显示状态
336 | - 检查状态,以及
337 | - 退出调试器
338 |
339 | raise continuation 是被包裹在异常状态内的 continuation,(如果有的话)。标准的异常报告过程、 `assert` ,`assert-violation` 、 `error`以及 `Chez Scheme` 的过程 `assertion-violationf`, `errorf` 和 `syntax-error` 都会抛出一个对应调用用异常状态包裹的 continuation。这使得程序员可以检查待判定程序出错、冲突、或者失败断言发生所在的帧。
340 |
341 | 另一种交互式调试器—中断处理器,可以通过键盘中断触发默认的键盘中断处理器或是显式调用过程 `break`
342 | 触发默认的中断处理器来进入。同样地,命令“?”会列出命令选项,包括以下命令:
343 |
344 | - 退出中断处理并继续
345 | - 重置当前的 `café` (译注:读取-求值-打印 循环)
346 | - 终止整个 Scheme 会话
347 | - 进入新的 `café`
348 | - 检查当前的 continuation 并且返回
349 | - 展示程序的统计信息 (运行时间以及内存使用情况)
350 |
351 | 通常来说可以通过输入 EOF(文件结束符)来退出调试器中断处理。在 Unix 下是“Control-D”,在 Windows 上是“Control-Z”。
352 |
353 | - 过程: (debug)
354 | - 返回值: 无返回值
355 | - 所属库: (chezscheme)
356 |
357 | 当默认的异常处理接收到严重或者非警告的异常状态,它会展示该状态并且重置当前的 `café`。在重置之前,它会将状态保存在参数 `debug-condition` 。 `debug` 过程可被用来检查状态。无论何时内建的错误报告机制抛出一个错误之后,当前错误抛出点的 continuation 都是可以被检查的。更广义地来说, `debug` 可以检查 `make-continuation-condition` 创建的任意 `continuation状态`中包含的 `continuation`。
358 |
359 | 如果参数 `debug-on-exception` 被设置为 `#t` ,对于所有的严重和非警告的状态,默认的异常处理机制会直接进入调试器。命令行参数 `--debug-on-exception` 可以用来从命令行将 `debug-on-exception` 设置为 `#t`,这对于调试脚本或者使用 `--script` 或 `--program` 命令行参数运行的顶层程序会非常有用。
360 |
361 | ## 3.3 交互式审查器
362 |
363 | 审查器可以直接通过调用过程 `inspect` 或者从调试器间接启动。它允许程序员检查循环对象、端口和过程这类没有读取器语法的对象、continuation 对象、程序员无法直接接触到的变量以及通常的可以被打印的Scheme对象。
364 |
365 | 审查器的首要目的是检查而不是修改对象。但是,可被赋值的变量的值有可能在审查器内被改变。可被赋值的变量通常被限制于赋值在源程序中出现的变量。也可以对于一个对象调用任意的过程(包括一些修改变量的过程例如 `set-car!` )那些原本就是不可改变的对象是不可以被改变的,例如不可被赋值的变量、过程以及大数(bignum),因为这么做违反了编译器和运行时的假定。
366 |
367 | 用户在使用时可以看到包括当前对象的打印展示的提示行,如果有必要的话会使用缩写让一行可以放得下。大量的命令可以用来展示当前的对象以及在对象内游走。审查器还提供了可用选项的“在线”描述。命令"?"会显示可以应用于当前对象的可用命令。命令"??" 会显示通用的命令。命令"h"会给出如何使用审查器的简要描述。EOF 字符或者命令"q"可以退出审查器。
368 |
369 | - 过程: (inspect obj)
370 | - 返回值: 不确定
371 | - 所属库: (chezscheme)
372 |
373 | 如上所述,在 `obj` 上调用审查器。依据当前的对象的类型,不同的审查器可用命令罗列如下:
374 |
375 | **通用命令**
376 |
377 | `help` 或 `h` 会显示关于如何使用审查器的简要描述
378 |
379 | `?` 展示当前对象对应的可用选项
380 |
381 | `??` 展示通用参数
382 |
383 | `print` 或 `p` 打印当前对象(使用 `pretty-print` )
384 |
385 | `wirte` 或 `w` 写下当前对象(使用 `write` )
386 |
387 | `size` 写下当前对象占用的字节数(通过 `compute-size` 确定),包括任意当前对象可以访问的对象,除了
388 | 同一个审查器会话中已经计算过的。
389 |
390 | `find expr[g]` 会求值 `expr` , `expr` 应当是一个接收一个参数的过程, `find` 命令会搜索当前对象中第一个出现的符合条件的对象(将此对象作为参数给 expr 会返回真)。搜索时将立即值(如:定长数),比 g 更老的值,以及在搜索中已经访问过的点作为叶子结点。如果 `g` 没有给定,那么会默认设置为当前最大代数,例如 `collect-maximum-generation` 。如果指定了那么, `g` 必须是小于等于当前最大代数(译者注:当前对象树的树高),或者符号 `static` 代表静态生成。如果找到了这个对象那么审查器的焦点会移动到对应的对象上,就像从原本的对象那里走了几步来到了目标对象,因此 `up` 命令可以确定相对于原本的对象目标对象要怎么找到。
391 |
392 | `find-next` 重复上一个 find,找到下一个符合条件的对象,如果有的话。
393 |
394 | `up` 或 `u n` 返回上 n 个层级。用于在检查对象中向外移动,n 默认是 1。
395 |
396 | `top` 或 `t` 返回最外层的检查对象。
397 |
398 | `forward` 或 `f` 移动到下 n 个表达式。用于在一个包含一系列元素的对象中从一个元素移动到另一个,例如,list、vector、
399 | record、frame 或 closure。n 默认是 1。
400 |
401 | `back` 或 `b` 向前移动 n 个表达式。用于在一个包含一系列元素的对象中从一个元素移动到另一个,list、vector、
402 | record、frame 或 closure。n 默认是 1。
403 |
404 | `=> expr` 将当前对象发送给过程 `expr` 。 `expr` 可以在当前行,也可以是接下来几行开始,可以占据多行。
405 |
406 | `file path` 打开对应路径的源代码文件。参数 `source-directories` (见 12.5 章)决定了源文件的搜索路径
407 |
408 | `list line count` ,罗列从第 `line` 行开始的 `count` 行源代码。 `line` 的默认值是上一次展示的最后一行,`count` 的默认值是 10 或者上一次展示的代码行数。如果 `line` 是负数那么, `list` 会展示上一次展示 `line` 行之前的代码。
409 |
410 | `files` 展示当前打开的源文件。
411 |
412 | `mark` 或 `m m` 使用符号 `m` 标记当前位置。如果 `m` 没有指定,会使用默认唯一标志标记当前位置。
413 |
414 | `goto` 或 `g m` 回到标记着 `m` 的位置。如果 `m` 没有指定,审查器会回到默认标志标记的位置。
415 |
416 | `new-cafe` 或 `n` 进入新的 读取-求值-打印 循环 (所谓的 café),可以得到通常的顶层环境。
417 |
418 | `quit` 或 `q` 从审查器退出。
419 |
420 | `reset` 或 `r` 重置当前的 `café` 。
421 |
422 | `abort` 或 `a x` 从 Scheme 退出,状态值设置为 X,默认值是-1。
423 |
424 | **Continuation 命令**
425 |
426 | `show-frames` 或 `sf` 显示接下来的第 n 个帧,如果 `n` 没有指定,就展示所有的帧。
427 |
428 | `depth` 展示 `continuation` 中的帧的数量
429 |
430 | `down` 或 `d n` 移动到 continuation 中的下 n 个帧,n 默认是 1
431 |
432 | `show` 或 `s` 展示 `continuation`(下一个帧),如果有的话,展示调用过程的源代码,待判定的调用源码,闭包,帧以及自由变量值。只有在编译对应的 lambda 表达式的时候启用了生成审查器信息时才可以得到源码。
433 |
434 | `show-local` 或 `sl` 和 `show` 或 `s` 类似除了不展示自由变量。(如果有自由变量可以通过检查闭包得到)。
435 |
436 | `length` 或 `l` 展示 continuation 中顶层帧的元素的数量。
437 |
438 | `ref` 或 `r` 移动到第 n 个或者命名的帧。 `n` 默认是 0。如果多个元素有同样的名字,只有一个可以通过名字获得,其他的必须使用数字。
439 |
440 | `code` 或 `c` 移动到调用过程的源代码。
441 |
442 | `call` 移动到待调用判定的源代码
443 |
444 | `file` 打开包含判定调用的源代码的源文件,如果知道的话。 参数 `source-directories` (见 12.5 章)决定了使用相对路径时搜索的文件夹的路径。
445 |
446 | 对于 `/` 开头的绝对路径(或者 Windows 使用 `\` ),审查器会先尝试绝对路径,然后在设定的源代码文件夹下尝试路径的最后一部分(文件名)。对于 `./` (Windows 下是 `.\`) 或 `../` (Windows 下是 `..\` ) 开头的路径名,审查器首先检查 `.` 或 `..` ,然后尝试设定的源代码文件夹中 `.` 或 `..` 开头的路径,最后是在源代码文件夹中搜索路径的最后一部分(文件名)。对于其他的(相对的)路径名,审查器会先在设定的源代码目录中寻找整个相对路径,然后是在设定的源代码目录中寻找最后的部分(文件名)。
447 |
448 | 如果找到了名字相同但是内容与原本的源文件不同的文件,这个文件会被跳过。这通常是因为文件在编译之后又被
449 | 修改过。给定一个明确的文件名作为参数来强制打开特定文件(参考上面的通用命令)。
450 |
451 | `eval` 或 `e expr` 在包含帧元素绑定的环境中对 `expr` 进行求值。在被求值的表达式内部,每一个帧元素 `n` 可以通过 `%n` 获取。同样的具名帧可以通过他们的名字得到。只有在编译对应的 lambda 表达式时启用了生成审查器信息时才可以得到名字。
452 |
453 | `set!` 或 `!n e` 设置第 n 个帧元素为 `e` ,如果那个元素是可以被赋值的。 `n` 默认是 0。
454 |
455 | **Procedure 命令(过程)**
456 |
457 | `show` 或 `s` 展示源代码和过程中的自由变量。只有在编译对应的 lambda 表达式时启用了生成审查器信息时才可以得到源代码。
458 |
459 | `code` 或 `c` 移动到过程的源代码
460 |
461 | `file` 打开包含源代码的文件,如果知道的话。更多信息参见上文对 continuation 的描述
462 |
463 | `length` 或 `l` 展示记录在过程对象中的自由变量。
464 |
465 | `ref` 或 `r` 移动到第 n 个或者是具名的自由变量。n 的默认值是 0。如果多个自由变量是命名相同那么只有一个可以通过名字获取,剩下的必须通过数字访问。
466 |
467 | `set!` 或 `! n e` 将第 `n` 个自由变量的值设置为 `e` ,前提是该变量可以被赋值。 `n` 默认是 0。
468 |
469 | `eval` 或 `e expr` 在包含该过程的绑定的环境中对 `expr` 求值。在被求值的表达式内部,每一个自由变量 `n` 的值可以通过变量 `%n` 来获取。具名的变量可以通过他们的名字获取到变量值。只有在编译对应的 lambda 表达式的时候启用了生成审查器信息才可以得到变量名。
470 |
471 | **Pair(list)命令(列表)**
472 |
473 | `show` 或 `s n` 显示 list 中的第 n 个元素,如果没有指定 n 那么所有元素都会被展示。
474 |
475 | `length` 或 `l` 展示 list 的长度。
476 |
477 | `car` 移动到当前对象的 car 对应的对象。
478 |
479 | `cdr` 移动到当前对象的 cdr 对应的队形。
480 |
481 | `ref` 或 `r n` 移动到当前 list 中的第 `n` 个元素。 `n` 默认是 0。
482 |
483 | `tail n` 移动到当前 list 的第 `n` 个 cdr。 `n` 默认是 1。
484 |
485 | **Vector,Bytevector,Fxvector 命令(向量)**
486 |
487 | `show` 或 `s n` ,展示当前 vector 的第 `n` 个元素,如果 `n` 没有指定就展示所有的元素。
488 |
489 | `length` 或 `l` 展示向量的长度。
490 |
491 | `ref` 或 `r n` 移动到当前 vector 的第 `n` 个元素, `n` 默认是 0。
492 |
493 | **String 命令(字符串)**
494 |
495 | `show` 或 `s n` 展示当前 string 的第 `n` 个元素,如果没有指定 `n` ,就展示所有的元素。
496 |
497 | `length` 或 `l` 展示当前 string 的长度。
498 |
499 | `ref` 或 `r n` 移动到当前 string 的第 `n` 个元素, `n` 默认是 0。
500 |
501 | `unicode n` 显示当前 string 的前 `n` 个元素对应的十六进制 Unicode 标量值。
502 |
503 | `ascii n` 展示 string 的前 `n` 个对象对应的十六进制 ASCII 编码值,使用 `--` 说明对应的字符的 Unicode 编码不在 ASCII 的范围内。
504 |
505 | **Symbol 命令(符号)**
506 |
507 | `show` 或 `s` 显示 symbol 的数据成员。
508 |
509 | `value` 或 `v` 移动到当前 symbol 顶层的值。
510 |
511 | `name` 或 `n` 移动到symbol的命名。
512 |
513 | `property-list` 或 `pl` 移动到 symbol 的属性列表。
514 |
515 | `ref` 或 `r n` 移动到 symbol 的第 `n` 个数据成员。第 0 个数据成员是该 symbol 的顶层值,第 1 个数据成员是 symbol 的名字,第2个数据成员是他的属性列表。n 默认是 0。
516 |
517 | **Character 命令(字符)**
518 |
519 | `unicode` 展示当前字符的十六进制 Unicode 编码值
520 |
521 | `ascii` 展示当前字符的 ASCII 编码值,如果 Unicode 编码值不在 ASCII 的编码范围内则使用 `--` 来代替
522 |
523 | **Box 命令**
524 |
525 | `show` 或 `s` 展示当前 box 的内容 。(译者注:box 像是只有一个元素的向量,通常用来存放最小可修改值)
526 |
527 | `unbox` 或 `ref` 或 `r` 移动到被装箱的对象。
528 |
529 | **Port 命令**
530 |
531 | `show` 或 `s` 展示当前端口的数据成员,包括输入和输出的大小,下标以及缓存数据成员。
532 |
533 | `name` 移动到当前端口的名字。
534 |
535 | `handler` 移动到当前端口的句柄。
536 |
537 | `output-buffer` 或 `ob` 移动到端口的输出缓存。
538 |
539 | `input-buffer` 或 `ib` 移动到端口的输入缓存。
540 |
541 | **Record 命令 (记录)**
542 |
543 | `show` 或 `s` 显示记录的内容。
544 |
545 | `fields` 移动到记录的数据成员名字列表。
546 |
547 | `name` 移动到记录的名字。
548 |
549 | `rtd` 移动到记录的记录类型描述。
550 |
551 | `ref` 或 `r name` 如果可以访问,移动到记录的具名数据成员。
552 |
553 | `set!` 或 `! name value` 修改记录对应名字的数据成员的值,如果该成员是可以被修改的。
554 |
555 | **Transport Link Cell (TLC) 命令**
556 |
557 | `show` 或 `s` 展示 TLC 的数据成员 。
558 |
559 | `keyval` 移动到 TLC 的 keyval。
560 |
561 | `tonc` 移动到 TLC 的 tconc
562 |
563 | `next` 移动到 TLC 的下一个的链接
564 |
565 | `ref` 或 `r n` 移动到符号的第 `n` 个数据成员。第 0 个数据成员是 keyval,第一个是 tconc,第二个是下一个的链接。 `n` 默认是 0。
566 |
567 | ## 3.4 对象审查器
568 |
569 | 通过组合不同的审查器的接口,系统还提供了非交互式的审查器。和交互式的审查器一样,也可以使用非交互式的方式来检查那些通常来说无法检查的对象。非交互式审查器遵循单一,面向对象的协议。一般的 Scheme 对象都是封装在过程或者审查器对象中的,这些过程或审查器对象接收一个符号性的信息然后返回关于被封装对象的信息或者是新的封装着对象一部分的审查器对象。
570 |
571 | - 过程: (inspect/object object)
572 | - 返回: 一个审查器对象过程
573 | - 所属库: (chezscheme)
574 |
575 | `inspect/object` 用于将一个 Scheme 对象转化为一个审查器对象。所有的审查器对象都接受 `type` 、 `print` 、`write` 和 `size` 这四个消息。 `type` 用于返回对象的类型的符号表示。 `print` 和 `write` 一定要和端口参数一起使用。这两条消息用于通过 Scheme 过程 `preety-print` 和 `write` 将对象的表示写到对应端口。 `size` 消息会返回当前对象所占用的字节大小的常数表示,包括任意当前对象可以访问的对象,但是除了已经使用同一个`inspect/object` 调用计算过大小的审查器对象。
576 |
577 | 除了变量审查器对象所有的审查器对象都接受消息 `value` ,这会返回当前审查器对象真正封装的对象。
578 |
579 | ```scheme
580 | (define x (inspect/object '(1 2 3)))
581 | (x 'type) => pair
582 | (define p (open-output-string))
583 | (x 'write p)
584 | (get-output-string p) => "(1 2 3)"
585 | (x 'length) => (proper 3)
586 | (define y (x 'car))
587 | (y 'type) \rightarrow simple
588 | (y 'value) => 1
589 | ```
590 |
591 | **Pair(序对)审查器对象** 序对审查器对象包含一个 Scheme 的序对。
592 |
593 | `(pair-object 'type)` 返回符号 `pair` 。
594 |
595 | `(pair-object 'car)` 返回审查器对象中包含的序对的 `car` 数据成员。
596 |
597 | `(pair-object 'cdr)` 返回审查器对象中包含的序对的 `cdr` 数据成员。
598 |
599 | `(pair-object 'length)` 返回语句形式的列表 (与类型有关). 类型部分包含符号 `proper` 、 `omproper`
600 | 或是 `circular`,这会依据列表结构的不同而不同。计数部分包含该列表中不同的序对的数量。
601 |
602 | **Box 审查器对象** Box 审查器对象包含 Chez Scheme 的 box。
603 |
604 | `(box-object 'type)` 返回符号 `box` 。
605 | `(box-object 'unbox)` 返回包含 box 内容的审查器对象。
606 |
607 | **TLC 审查器对象** Box 审查器对象包含 Chez Scheme 的 box。
608 |
609 | `(tlc-object 'type)` 返回符号 `tlc` 。
610 |
611 | `(tlc-object 'keyval)` 返回一个审查器对象,包含 TLC 的 keyval。
612 |
613 | `(tlc-object 'tconc)` 返回一个审查器对象,包含 TLC 的 tconc。
614 |
615 | `(tlc-object 'next)` 返回一个审查器对象,包含 TLC 的 下一个链接。
616 |
617 | **Vector, String, Bytevector, 和 Fxvector 审查器对象** .向量(bytevector, string, fxvector)审查器对象包含 Scheme 的向量(bytevectors, strings, fxvectors)。
618 |
619 | `(vector-object 'type)` 返回符号 `vector` ( `string`, `bytevector`, `fxvector`).
620 |
621 | `(vector-object 'length)` 返回向量或字符串的元素数量。
622 |
623 | `(vector-object 'ref n)` 返回一个审查器对象,包含向量或者字符串的第 `n` 个元素。
624 |
625 | **简单审查器对象** ,简单审查器对象包含非结构化,不能修改的对象。包括数字、布尔值、空表、EOF 对象以及 void 对象。
626 | 可以直接通过给对象一个 `value` 参数来检查。
627 |
628 | `(simple-object 'type)` 返回符号 `symbol` 。
629 |
630 | **未绑定审查器对象** ,虽然 Scheme 程序通常无法访问未绑定的对象,但在检查变量时可能会遇到它们。
631 |
632 | `(unbound-object 'type)` 返回符号 `unbound`。
633 |
634 | **过程审查器对象** 过程审查器对象包含一个 Scheme 过程。
635 |
636 | `(procedure-object 'type)` 返回符号 `procedure` 。
637 |
638 | `(procedure-object 'length)` 返回自由变量的数量 。
639 |
640 | `(procedure-object 'ref n)` 返回一个审查器对象,包含过程的第 n 个自由变量。详见下面的变量审查器的部分。n必须是非负数且小于过程中自由变量的数量。
641 |
642 | `(procedure-object 'eval expr)` 对 `expr` 求值并返回其值。过程中的自由变量在被求值的表达式中使用标识符
643 | %n 表示,n 是审查器显示的位置数字。具名的变量同样也绑定到他们本身的名字上。
644 |
645 | `(procedure-object 'code)` 返回一个审查器对象,其中包含过程的代码对象。关于代码审查器对象详见下文。
646 |
647 | **continuation 审查器对象** 通过 `call/cc` 产生的 continuation 实际上是过程。但是在检查这样一个过程时,有可能会需要将 continuation 的底层数据结构暴露出来才行。一个 continuation 的数据结构包含要恢复计算的位置,执行计算需要用到的变量以及下一个 continuation 的链接。
648 |
649 | `(continuation-object 'type)` 返回符号 `continuation` 。
650 |
651 | `(continuation-object 'length)` 返回自由变量的数量。
652 |
653 | `(continuation-object 'ref n)` 返回一个审查器对象,包含 continuation 的第 n 个自由变量。详见下面的变量审查器的部分。n必须是非负数且小于过程中自由变量的数量。
654 |
655 | `(continuation-object 'eval expr)` 对 `expr` 求值并返回其值。continuation 帧中的自由变量在被求值的表达式中使用标识符`%n` 表示,n 是审查器显示的位置数字。具名的变量同样也绑定到他们本身的名字上。
656 |
657 | `(continuation-object 'code)` 返回一个审查器对象,返回过程的代码对象,此代码是当前的 continuation 帧被创建时对应过程是正在运行的。详见下文有关代码审查器对象。
658 |
659 | `(continuation-object 'depth)` 返回当前 continuation 包含的帧的数量。
660 |
661 | `(continuation-object 'link)` 返回一个审查器对象,其中包含下一个 continuation 帧。深度必须大于 1。
662 |
663 | `(continuation-object 'link* n)` 返回一个审查器对象,其中包含 continuation 的第 n 个链接,n 必须小于深度。
664 |
665 | `(continuation-object 'source)` 返回一个审查器对象,其中包含与 continuation 相关的源代码的信息(程序中导致 continuation 形成的源码)。如果没有附加源码信息那么返回 `#f` 。
666 |
667 | `(continuation-object 'source-object)` 返回一个审查器对象,其中包含产生 continuation 形成相关的源码对象,如果没有附加源码对象那么返回 `#f` 。
668 |
669 | `(continuation-object 'source-path)` 尝试寻找包含产生 continuation 过程所在的源码文件的路径。如果找到了就用三个值来标记文件内的位置: `path` 、 `line` 以及 `char` 。如果文件名知道但是对应名字的文件找不到就返回两个值,文件名和字符的绝对位置。如果文件在编译后被修改了,即使找到了该文件依然是作为搜索失败处理。如果不知道文件名那么没有返回值。参数 `source-directories` 决定了使用相对路径时源文件的搜索目录。
670 |
671 | **Code(代码) 审查器对象** 一个审查器对象包含 Chez Scheme 的代码对象
672 |
673 | `(code-object 'type)` 返回一个符号 `code`
674 |
675 | `(code-object 'name)` 返回一个字符串或者 `#f` 。代码审查器对象的名字就是对应过程原本绑定或者是被赋值时的名字。因为变量的绑定可以被改变所以这个名字可能会对不上。 如果审查器不能获悉过程的名字就返回 `#f` 。
676 |
677 | `(code-object 'source)` 返回代码对象附加的源码信息,如果没有源码信息就返回 #f。
678 |
679 | `(code-object 'source-object)` 返回一个审查器对象,其中包含代码对象的源对象,如果没有附加源对象则返回 #f。
680 |
681 | `(code-object 'source-path)` 尝试寻找包含产生产生对应过程的 lambda 表达式所在的源文件的文件名。如果找到了就用三个值来标记文件内的位置: `path` 、 `line` 以及 `char` 。如果文件名知道但是对应名字的文件找不到就返回两个值,文件名和字符的绝对位置。如果文件在编译后被修改了,即使找到了该文件依然是作为搜索失败处理。如果不知道文件名那么没有返回值。参数 `source-directories` 决定了使用相对路径时源文件的搜索目录。
682 |
683 | `(code-object 'free-count)` 返回对应代码的任意过程中自由变量的数量。
684 |
685 | **Variable(变量)审查器对象** 。 变量审查器对象封装了变量绑定。尽管真正的底层表示可能会不同,变量审查器对象提供了统一的界面。
686 |
687 | `(variable-object 'type)` 返回符号 `variable`
688 |
689 | `(variable-object 'name)` 返回符号或者 `#f` 。如果无法获得名字或者名字是编译器生成的临时变量则返回 `#f` 。如果在编译时期
690 | `generate-inspector-information` 为假则变量名称不会被保留。
691 |
692 | `(variable-object 'ref)` 返回一个审查器对象,其中包含变量的当前值。
693 |
694 | `(variable-object 'set! e)` 将当前的变量值设定为 `e` , 返回值不确定。如果变量不能被赋值则抛出一个状态类型为`&assertion` 的异常。
695 |
696 | **Port(端口) 审查器对象** 包含端口的端口审查器对象。
697 |
698 | `(port-object 'type)` 返回符号 `port`
699 |
700 | `(port-object 'input?)` 如果一个端口是输入端口则返回 `#t` ,否则返回 `#f` 。
701 |
702 | `(port-object 'output?)` 如果一个端口是输出端口则返回 `#t` ,否则返回 `#f` 。
703 |
704 | `(port-object 'binary?)` 如果一的端口是二进制端口则返回 `#t` ,否则返回 `#f` 。
705 |
706 | `(port-object 'closed?)` 如果端口关闭了则返回 `#t` ,如果开着就返回 `#f` 。
707 |
708 | `(port-object 'name)` 返回一个审查器对象包含端口的名字。
709 |
710 | `(port-object 'handler)` 返回一个过程审查器对象,其中封装了端口的句柄,就像 `port-handler` 会返回的那样。
711 |
712 | `(port-object 'output-size)` 如果是一个输出端口的话返回一个表示输出缓冲区大小的定长数,否则的话返回值是不确定的。
713 |
714 | `(port-object 'output-index)` 如果是一个输出端口则将缓冲区下标作为一个定长数返回,否则是个不确定的值。
715 |
716 | `(port-object 'output-buffer)` 返回一个审查器对象,其中包含用于输出缓冲的字符串。
717 |
718 | `(port-object 'input-size)` 如果是一个输入端口的话返回一个输出缓冲区大小的定长数,否则的话返回值是不确定的。
719 |
720 | `(port-object 'input-index)` 如果是一个输入端口则返回一个输入缓冲区大小的定长数,否则返回值不确定。
721 |
722 | `(port-object 'input-buffer)` 返回一个审查器对象 ,其中包含用于输入缓冲的字符串。
723 |
724 | **Symbol(符号)审查器对象** 。 符号审查器对象包含符号,包活 gensyms(生成符号)
725 |
726 | `(symbol-object 'type)` 返回符号 `symbol`。
727 |
728 | `(symbol-object 'name)` 返回一个字符串审查器对象。一个符号审查器的的字符串表示的名字就是该符号的被打印时的表示。就像过程`symbol->string` 会返回的那样。
729 |
730 | `(symbol-object 'gensym?)` 如果一个符号是生成符号则返回 `#t` ,否则返货 `#f` 。生成符号使用 `gensym` 生成。
731 |
732 | `(symbol-object 'top-level-value)` 返回一个审查器对象 ,其中包含符号的全局值。
733 |
734 | `(symbol-object 'property-list)` 返回一个审查器对象,其中包含符号的属性列表。
735 |
736 | **Record(记录)审查器对象** 。记录审查器对象中包含记录。
737 |
738 | `(record-object 'type)` 返回符号 `record`
739 |
740 | `(record-object 'name)` 返回一个字符串审查器对象,其中包含记录类型的名字。
741 |
742 | `(record-object 'fields)` 返回一个审查器对象,其中包括记录类型的成员名字列表。
743 |
744 | `(record-object 'length)` 返回数据成员的数量。
745 |
746 | `(record-object 'rtd)` 返回一个审查器对象,其中包含记录类型的记录类型描述符。
747 |
748 | `(record-object 'accessible? name)` 如果对应名字的数据成员是可以访问的就返回 `#t` ,否则返回 `#f` 。如果编译器优化过数据成员有可能不能被访问。
749 |
750 | `(record-object 'ref name)` 返回一个审查器对象,其中包括对应名字数据成员的值。如果对应数据成员不可访问则会抛出状态类型为`&assertion` 的异常。
751 |
752 | `(record-object 'mutable? name)` 如果对应名字的数据成员是可以被改变的则返回 `#t` ,否则返回 `#f` 。如果一个数据成员没有申明为可改变的那么就是不可更改的。如果编译器将所有的数据成员的赋值都优化掉了那么他们也是不可更改的。
753 |
754 | `(record-object 'set! name value)` 将对应名字的数据成员设置为 `value` 。如果一个数据成员不能被赋值就抛出状态类型为 `&assertion` 的异常。
755 |
756 | ## 3.5 定位对象
757 |
758 | - 过程: (make-object-finder pred)
759 | - 过程: (make-object-finder pred g)
760 | - 过程: (make-object-finder pred x g)
761 | - 返回: 见下文
762 | - 所属库: (chezscheme)
763 |
764 | 过程 `make-object-finder` 接受一个谓词过程 `pred` 以及两个可选参数: 一个开始点 `x` 和一个最大代数 `g` 。开始点的位置默认是过程 `oblist` 的值,最大代数的默认值由参数 `collection-maximum-generation` 决定。 `make-object-finder` 返回一个对象检索器`p`,可以用来搜寻从起始点 `x` 开始,满足 `pred` 的对象。立即对象和世代比 g 大的对象会被视为叶子节点。 `p` 是一个不接受参数的过程。如果从 `x` 开始能找到一个满足 `pred` 的对象 y, `p` 就会返回一个列表,列表的第一个值是 `y`,剩下的元素代表从 x 开始到 y 的途径对象,列表是逆序的。 `p` 可以被调用多次来找到其他满足满足谓词过程的对象,如果有的话。如果没有更多满足条件的对象就返回 `#f` 。
765 |
766 | `p` 会在维护一个内部状态用于记录它搜索过哪些地方,这样当搜索再次开始的时候他不会两次返回同一个对象。这个状态可以是起始点对象和所有能从 x 访问的对象的好几倍。
767 |
768 | 交互式的审查器提供了 `find` 和 `find-next` 这些方便的方式来寻找对象。
769 |
770 | 默认情况下,静态代码的重定位表是被丢弃掉的,这会造成对象查找器在涉及到静态代码对象时无法提供准确的结果。也就是说,无法直接从已经提升为静态生成的代码中找到任何代码对象。 如果这是一个问题,可以使用命令行参数 `--retain-static-relocation`防止重定位表被丢弃。
771 |
772 | ## 3.6 嵌套对象的大小和组成
773 |
774 | 过程 `compute-size` 和 `compute-composition` 可以用于知晓一个对象的大小或组成,包括任何从该对象可访问的对象。根据从对象可到达的对象数量,这些过程可能会消耗大量内存。在了解堆中所有对象的数量,大小,代数和类型的应用程序中, `object-counts` 可能效率更高。
775 |
776 | 这些过程将立即对象(如 定长数,布尔值和字符)视为不计数,零字节的叶子节点。
777 |
778 | 默认情况下,这些过程还将静态对象(初始堆中的对象)视为不计数,零字节的叶子节点。
779 | 两个过程都接受一个可选的第二个参数,该参数指定计算时最大的代数,其中符号 `static` 用于表示静态代。
780 |
781 | 对象有时会指向比预期更多的对象。例如,如果将静态数据包含在内,过程 `(lambda (x) x)` 会间接指向异常处理系子系统(由于参数数量检查),以及其他诸如此类的对象。
782 |
783 | 默认情况下,静态代码的重定位表是被丢弃掉的,这会造成对象查找器在涉及到静态代码对象时无法提供准确的结果。也就是说,无法直接从已经提升为静态生成的代码中找到任何代码对象。 如果需要静态代码对象的精确大小和组成的话,使用命令行参数 `--retain-static-relocation`防止重定位表被丢弃。
784 |
785 | - 过程: (compute-size object)
786 | - 过程: (compute-size object generation)
787 | - 返回: 见下文
788 | - 所属库: (chezscheme)
789 |
790 | `object` 可以是任意对象, `generation` 必须是一个 0 到 `collect-maximum-generation` 之间的定长数,或者符号 `static`。如果 `generation` 没有指定,默认就是 `collect-maximum-generation` 的值。
791 | `compute-size` 返回 `object` 以及从它开始可访问的所有代数小于等于 `generation` 的对象在内存中占据的以字节计算的大小。立即值例如定长数,布尔值以及字符的大小为 0。
792 |
793 | 以下例子适用于 32 位指针的机器。
794 |
795 | ```scheme
796 | (compute-size 0) => 0
797 | (compute-size (cons 0 0)) => 8
798 | (compute-size (cons (vector #t #f) 0)) => 24
799 |
800 | (compute-size
801 | (let ([x (cons 0 0)])
802 | (set-car! x x)
803 | (set-cdr! x x)
804 | x)) => 8
805 |
806 | (define-record-type frob (fields x))
807 | (collect 1 1) ; force rtd into generation 1
808 | (compute-size
809 | (let ([x (make-frob 0)])
810 | (cons x x))
811 | 0) => 16
812 | ```
813 |
814 | - 过程: (compute-composition object)
815 | - 过程: (compute-composition object generation)
816 | - 返回值: 见下文
817 | - 所属库: (chezscheme)
818 |
819 | `object` 可以是任意对象。 `generation` 必须是一个 0 到 `collect-maximum-generation` 之间的定长数,或者符号 `static`。如果 `generation` 没有指定,默认就是 `collect-maximum-generation` 的值。
820 | `compute-composition` 返回一个关联列表用于表示 `object` 的组成 , 包括任何从当前对象开始可以访问的代数小于或等于`generation` 的对象 。关联列表的结构如下:
821 |
822 | ```scheme
823 | ((type count . bytes) ...)
824 | ```
825 |
826 | `type` 是原始类型的名字(使用符号表示,例如 `pair` )或是一个记录类型描述符(rtd)。 `count` 和 `bytes` 都是非负定长数。
827 |
828 | 立即对象例如常数、布尔值和字符并不包括在组成内。
829 |
830 | 以下例子适用于 32 位指针的机器。
831 |
832 | ```scheme
833 | (compute-composition 0) => ()
834 | (compute-composition (cons 0 0)) => ((pair 1 . 8))
835 | (compute-composition
836 | (cons (vector #t #f) 0)) => ((pair 1 . 8) (vector 1 . 16))
837 |
838 | (compute-composition
839 | (let ([x (cons 0 0)])
840 | (set-car! x x)
841 | (set-cdr! x x)
842 | x)) => ((pair 1 . 8)
843 |
844 | (define-record-type frob (fields x))
845 | (collect 1 1) ; force rtd into generation 1
846 | (compute-composition
847 | (let ([x (make-frob 0)])
848 | (cons x x))
849 | 0) => ((pair 1 . 8)
850 | (# 1 . 8))
851 | ```
852 |
853 |
--------------------------------------------------------------------------------
/05.BindingForms.md:
--------------------------------------------------------------------------------
1 |
2 | Chapter 5. Binding Forms
3 | This chapter describes Chez Scheme extensions to the set of Revised6 Report binding forms. See Chapter 4 of The Scheme Programming Language, 4th Edition or the Revised6 Report for a description of standard binding forms.
4 |
5 | Section 5.1. Definitions
6 | A definition in Revised6 Report Scheme is a variable definition, keyword definition, or derived definition, i.e., a syntactic extension that expands into a definition. In addition, the forms within a begin expression appearing after a sequence of definitions is spliced onto the end of the sequence of definitions so that definitions at the front of the begin expression are treated as if they were part of the outer sequence of definitions. A let-syntax or letrec-syntax form is treated similarly, so that definitions at the front of the body are treated as if they were part of the outer sequence of definitions, albeit scoped where the bindings of the let-syntax or letrec-syntax form are visible.
7 |
8 | Chez Scheme extends the set of definitions to include module forms, import forms, import-only forms, meta definitions, and alias forms, although the module, import, import-only, meta, and alias keywords are not available in a library or RNRS top-level program unless the scheme library is included in the library or top-level programs imports. These forms are described in Chapter 11.
9 |
10 | In Revised6 Report Scheme, definitions can appear at the front of a lambda or similar body (e.g., a let or letrec body), at the front of a library body, or intermixed with expressions within an RNRS top-level program body. In Chez Scheme, definitions may also be used in the interactive top-level, i.e., they can be intermixed with expressions in the REPL or in program text to be loaded from a file via load (Section 12.4). The Revised6 Report does not mandate the existence nor specify the semantics of an interactive top-level, nor of a load procedure.
11 |
12 | The macro expander uses the same two-pass algorithm for expanding top-level begin expressions as it uses for a lambda, library, or top-level program body. (This algorithm is described in Section 8.1 of The Scheme Programming Language, 4th Edition.) As a result,
13 |
14 | (begin
15 | (define-syntax a (identifier-syntax 3))
16 | (define x a))
17 |
18 | and
19 |
20 | (begin
21 | (define x a)
22 | (define-syntax a (identifier-syntax 3)))
23 |
24 | both result in the giving x the value 3, even though an unbound variable reference to a would result if the two forms within the latter begin expression were run independently at top level.
25 |
26 | Similarly, the begin form produced by a use of
27 |
28 | (define-syntax define-constant
29 | (syntax-rules ()
30 | [(_ x e)
31 | (begin
32 | (define t e)
33 | (define-syntax x (identifier-syntax t)))]))
34 |
35 | and the begin form produced by a use of
36 |
37 | (define-syntax define-constant
38 | (syntax-rules ()
39 | [(_ x e)
40 | (begin
41 | (define-syntax x (identifier-syntax t))
42 | (define t e))]))
43 |
44 | are equivalent.
45 |
46 | The Revised6 Report specifies that internal variable definitions be treated like letrec*, while earlier reports required internal variable definitions to be treated like letrec. By default, Chez Scheme implements the Revised6 Report semantics for internal variable definitions, as for all other things, but this behavior may be overridden via the internal-defines-as-letrec* parameter.
47 |
48 | thread parameter: internal-defines-as-letrec*
49 | libraries: (chezscheme)
50 |
51 | When this parameter is set to #t (the default), internal variable definitions are evaluated using letrec* semantics. It may be set to #f to revert to the letrec semantics for internal variable definitions, for backward compatibility.
52 |
53 | Section 5.2. Multiple-value Definitions
54 | syntax: (define-values formals expr)
55 | libraries: (chezscheme)
56 |
57 | A define-values form is a definition and can appear anywhere other definitions can appear. It is like a define form but permits an arbitrary formals list (like lambda) on the left-hand side. It evaluates expr and binds the variables appearing in formals to the resulting values, in the same manner as the formal parameters of a procedure are bound to its arguments.
58 |
59 | (let ()
60 | (define-values (x y) (values 1 2))
61 | (list x y)) (1 2)
62 | (let ()
63 | (define-values (x y . z) (values 1 2 3 4))
64 | (list x y z)) (1 2 (3 4))
65 |
66 | A define-values form expands into a sequence of definitions, the first for a hidden temporary bound to a data structure holding the values returned by expr and the remainder binding each of the formals to the corresponding value or list of values, extracted from the data structure via a reference to the temporary. Because the temporary must be defined before the other variables are defined, this works for internal define-values forms only if internal-defines-as-letrec* is set to the default value #t.
67 |
68 | Section 5.3. Recursive Bindings
69 | syntax: (rec var expr)
70 | returns: value of expr
71 | libraries: (chezscheme)
72 |
73 | The syntactic form rec creates a recursive object from expr by establishing a binding of var within expr to the value of expr. In essence, it is a special case of letrec for self-recursive objects.
74 |
75 | This form is useful for creating recursive objects (especially procedures) that do not depend on external variables for the recursion, which are sometimes undesirable because the external bindings can change. For example, a recursive procedure defined at top level depends on the value of the top-level variable given as its name. If the value of this variable should change, the meaning of the procedure itself would change. If the procedure is defined instead with rec, its meaning is independent of the variable to which it is bound.
76 |
77 | (map (rec sum
78 | (lambda (x)
79 | (if (= x 0)
80 | 0
81 | (+ x (sum (- x 1))))))
82 | '(0 1 2 3 4 5)) (0 1 3 6 10 15)
83 |
84 | (define cycle
85 | (rec self
86 | (list (lambda () self))))
87 |
88 | (eq? ((car cycle)) cycle) #t
89 |
90 | The definition below expands rec in terms of letrec.
91 |
92 | (define-syntax rec
93 | (syntax-rules ()
94 | [(_ x e) (letrec ((x e)) x)]))
95 |
96 | Section 5.4. Fluid Bindings
97 | syntax: (fluid-let ((var expr) ...) body1 body2 ...)
98 | returns: the values of the body body1 body2 ...
99 | libraries: (chezscheme)
100 |
101 | The syntactic form fluid-let provides a way to temporarily assign values to a set of variables. The new values are in effect only during the evaluation of the body of the fluid-let expression. The scopes of the variables are not determined by fluid-let; as with set!, the variables must be bound at top level or by an enclosing lambda or other binding form. It is possible, therefore, to control the scope of a variable with lambda or let while establishing a temporary value with fluid-let.
102 |
103 | Although it is similar in appearance to let, its operation is more like that of set!. Each var is assigned, as with set!, to the value of the corresponding expr within the body body1 body2 .... Should the body exit normally or by invoking a continuation made outside of the body (see call/cc), the values in effect before the bindings were changed are restored. Should control return back to the body by the invocation of a continuation created within the body, the bindings are changed once again to the values in effect when the body last exited.
104 |
105 | Fluid bindings are most useful for maintaining variables that must be shared by a group of procedures. Upon entry to the group of procedures, the shared variables are fluidly bound to a new set of initial values so that on exit the original values are restored automatically. In this way, the group of procedures itself can be reentrant; it may call itself directly or indirectly without affecting the values of its shared variables.
106 |
107 | Fluid bindings are similar to special bindings in Common Lisp [30], except that (1) there is a single namespace for both lexical and fluid bindings, and (2) the scope of a fluidly bound variable is not necessarily global.
108 |
109 | (let ([x 3])
110 | (+ (fluid-let ([x 5])
111 | x)
112 | x)) 8
113 |
114 | (let ([x 'a])
115 | (letrec ([f (lambda (y) (cons x y))])
116 | (fluid-let ([x 'b])
117 | (f 'c)))) (b . c)
118 |
119 | (let ([x 'a])
120 | (call/cc
121 | (lambda (k)
122 | (fluid-let ([x 'b])
123 | (letrec ([f (lambda (y) (k '*))])
124 | (f '*)))))
125 | x) a
126 |
127 | fluid-let may be defined in terms of dynamic-wind as follows.
128 |
129 | (define-syntax fluid-let
130 | (lambda (x)
131 | (syntax-case x ()
132 | [(_ () b1 b2 ...) #'(let () b1 b2 ...)]
133 | [(_ ((x e) ...) b1 b2 ...)
134 | (andmap identifier? #'(x ...))
135 | (with-syntax ([(y ...) (generate-temporaries #'(x ...))])
136 | #'(let ([y e] ...)
137 | (let ([swap (lambda ()
138 | (let ([t x]) (set! x y) (set! y t))
139 | ...)])
140 | (dynamic-wind swap (lambda () b1 b2 ...) swap))))])))
141 |
142 | Section 5.5. Top-Level Bindings
143 | The procedures described in this section allow the direct manipulation of top-level bindings for variables and keywords. They are intended primarily to support the definition of interpreters or compilers for Scheme in Scheme but may be used to access or alter top-level bindings anywhere within a program whether at top level or not.
144 |
145 | procedure: (define-top-level-value symbol obj)
146 | procedure: (define-top-level-value symbol obj env)
147 | returns: unspecified
148 | libraries: (chezscheme)
149 |
150 | define-top-level-value is used to establish a binding for the variable named by symbol to the value obj in the environment env. If env is not provided, it defaults to the value of interaction-environment, i.e., the top-level evaluation environment (Section 12.3).
151 |
152 | An exception is raised with condition type &assertion if env is not mutable.
153 |
154 | A call to define-top-level-value is similar to a top-level define form, except that a call to define-top-level-value need not occur at top-level and the variable for which the binding is to be established can be determined at run time, as can the environment.
155 |
156 | (begin
157 | (define-top-level-value 'xyz "hi")
158 | xyz) "hi"
159 |
160 | (let ([var 'xyz])
161 | (define-top-level-value var "mom")
162 | (list var xyz)) (xyz "mom")
163 |
164 | procedure: (set-top-level-value! symbol obj)
165 | procedure: (set-top-level-value! symbol obj env)
166 | returns: unspecified
167 | libraries: (chezscheme)
168 |
169 | set-top-level-value! assigns the variable named by symbol to the value obj in the environment env. If env is not provided, it defaults to the value of interaction-environment, i.e., the top-level evaluation environment (Section 12.3).
170 |
171 | An exception is raised with condition type &assertion if the identifier named by symbol is not defined as a variable in env or if the variable or environment is not mutable.
172 |
173 | set-top-level-value! is similar to set! when set! is used on top-level variables except that the variable to be assigned can be determined at run time, as can the environment.
174 |
175 | (let ([v (let ([cons list])
176 | (set-top-level-value! 'cons +)
177 | (cons 3 4))])
178 | (list v (cons 3 4))) ((3 4) 7)
179 |
180 | procedure: (top-level-value symbol)
181 | procedure: (top-level-value symbol env)
182 | returns: the top-level value of the variable named by symbol in env
183 | libraries: (chezscheme)
184 |
185 | If env is not provided, it defaults to the value of interaction-environment, i.e., the top-level evaluation environment (Section 12.3).
186 |
187 | An exception is raised with condition type &assertion if the identifier named by symbol is not defined as a variable in env.
188 |
189 | top-level-value is similar to a top-level variable reference except that the variable to be referenced can be determined at run time, as can the environment.
190 |
191 | (let ([cons +])
192 | (list (cons 3 4)
193 | ((top-level-value 'cons) 3 4))) (7 (3 . 4))
194 |
195 | (define e (copy-environment (scheme-environment)))
196 | (define-top-level-value 'pi 3.14 e)
197 | (top-level-value 'pi e) 3.14
198 | (set-top-level-value! 'pi 3.1416 e)
199 | (top-level-value 'pi e) 3.1416
200 |
201 | procedure: (top-level-bound? symbol)
202 | procedure: (top-level-bound? symbol env)
203 | returns: #t if symbol is defined as a variable in env, #f otherwise
204 | libraries: (chezscheme)
205 |
206 | If env is not provided, it defaults to the value of interaction-environment, i.e., the top-level evaluation environment (Section 12.3).
207 |
208 | This predicate is useful in an interpreter to check for the existence of a top-level binding before requesting the value with top-level-value.
209 |
210 | (top-level-bound? 'xyz) #f
211 |
212 | (begin
213 | (define-top-level-value 'xyz 3)
214 | (top-level-bound? 'xyz)) #t
215 |
216 | (define e (copy-environment (interaction-environment)))
217 | (define-top-level-value 'pi 3.14 e)
218 | (top-level-bound? 'pi) #f
219 | (top-level-bound? 'pi e) #t
220 |
221 | procedure: (top-level-mutable? symbol)
222 | procedure: (top-level-mutable? symbol env)
223 | returns: #t if symbol is mutable in env, #f otherwise
224 | libraries: (chezscheme)
225 |
226 | If env is not provided, it defaults to the value of interaction-environment, i.e., the top-level evaluation environment (Section 12.3).
227 |
228 | This predicate is useful in an interpreter to check whether a variable can be assigned before assigning it with set-top-level-value!.
229 |
230 | (define xyz 3)
231 | (top-level-mutable? 'xyz) #t
232 | (set-top-level-value! 'xyz 4)
233 | (top-level-value 'xyz) 4
234 |
235 | (define e (copy-environment (interaction-environment) #f))
236 | (top-level-mutable? 'xyz e) #f
237 | (set-top-level-value! 'xyz e) exception: xyz is immutable
238 |
239 | procedure: (define-top-level-syntax symbol obj)
240 | procedure: (define-top-level-syntax symbol obj env)
241 | returns: unspecified
242 | libraries: (chezscheme)
243 |
244 | define-top-level-syntax is used to establish a top-level binding for the identifier named by symbol to the value of obj in the environment env. The value must be a procedure, the result of a call to make-variable-transformer, or the result of a call to top-level-syntax. If env is not provided, it defaults to the value of interaction-environment, i.e., the top-level evaluation environment (Section 12.3).
245 |
246 | An exception is raised with condition type &assertion if env is not mutable.
247 |
248 | A call to define-top-level-syntax is similar to a top-level define-syntax form, except that a call to define-top-level-syntax need not occur at top-level and the identifier for which the binding is to be established can be determined at run time, as can the environment.
249 |
250 | (define-top-level-syntax 'let1
251 | (syntax-rules ()
252 | [(_ x e b1 b2 ...) (let ([x e]) b1 b2 ...)]))
253 | (let1 a 3 (+ a 1)) 4
254 |
255 | define-top-level-syntax can also be used to attach to an identifier arbitrary compile-time bindings obtained via top-level-syntax.
256 |
257 | procedure: (top-level-syntax symbol)
258 | procedure: (top-level-syntax symbol env)
259 | returns: unspecified
260 | libraries: (chezscheme)
261 |
262 | top-level-syntax is used to retrieve the transformer, compile-time value, or other compile-time binding to which the identifier named by symbol is bound in the environment env. If env is not provided, it defaults to the value of interaction-environment, i.e., the top-level evaluation environment (Section 12.3). All identifiers bound in an environment have compile-time bindings, including variables.
263 |
264 | An exception is raised with condition type &assertion if the identifier named by symbol is not defined as a keyword in env.
265 |
266 | (define-top-level-syntax 'also-let (top-level-syntax 'let))
267 | (also-let ([x 3] [y 4]) (+ x y)) 7
268 |
269 | (define foo 17)
270 | (define-top-level-syntax 'also-foo (top-level-syntax 'foo))
271 | also-foo 17
272 | (set! also-foo 23)
273 | also-foo 23
274 | foo 23
275 |
276 | The effect of the last example can be had more clearly with alias:
277 |
278 | (define foo 17)
279 | (alias also-foo foo)
280 | also-foo 17
281 | (set! also-foo 23)
282 | also-foo 23
283 | foo 23
284 |
285 | procedure: (top-level-syntax? symbol)
286 | procedure: (top-level-syntax? symbol env)
287 | returns: #t if symbol is bound as a keyword in env, #f otherwise
288 | libraries: (chezscheme)
289 |
290 | If env is not provided, it defaults to the value of interaction-environment, i.e., the top-level evaluation environment (Section 12.3).
291 |
292 | All identifiers bound in an environment have compile-time bindings, including variables, so this predicate amounts to a bound check, but is more general than top-level-bound?, which returns true only for bound variables.
293 |
294 | (define xyz 'hello)
295 | (top-level-syntax? 'cons) #t
296 | (top-level-syntax? 'lambda) #t
297 | (top-level-syntax? 'hello) #t
298 |
299 | (top-level-syntax? 'cons (scheme-environment)) #t
300 | (top-level-syntax? 'lambda (scheme-environment)) #t
301 | (top-level-syntax? 'hello (scheme-environment)) #f
302 |
303 | Chez Scheme Version 9 User's Guide
304 | Copyright © 2019 Cisco Systems, Inc.
305 | Licensed under the Apache License Version 2.0 (full copyright notice.).
306 | Revised January 2020 for Chez Scheme Version 9.5.3
307 | about this book
308 |
309 |
--------------------------------------------------------------------------------
/06.ControlStructures.md:
--------------------------------------------------------------------------------
1 | # 6 控制结构
2 |
3 | ### 6.3 延续
4 |
5 | ### 6.4 引擎
6 |
7 | 引擎是一个支持定时抢占[15,24]的高级过程抽象。引擎可能被用来模拟多任务处理,实现操作系统内核,并执行非确定性的计算。
8 |
9 | 过程:(make-engine thunk)
10 |
11 | 返回:一个引擎
12 |
13 | 库:(chezscheme)
14 |
15 | 引擎是通过传递一个thunk(无参数过程)来制作引擎而创建的。thunk的主体是由引擎执行的计算。引擎本身是一个三参数的过程:
16 |
17 | ticks:一个正整数,指定引擎的运行时间,一个引擎会执行直到ticks用完,或计算完成。
18 |
19 | complete:一个或多个参数的过程,指定在计算完成时要执行的操作。它的参数是剩余ticks和计算产生的数值。
20 |
21 | expire:一个参数的过程,指定在计算完成前如果ticks耗尽该怎么做。它的参数是能够从中断处重新开始计算的新引擎。
22 |
23 | 当一个引擎应用于它的参数时,它被设定了一个以ticks的计时器。(参见320页 set-timer)如果在ticks耗尽之前引擎计算完成,系统将调用complete,并将剩余的ticks和计算结果生成的值传递给它。如果在计算完成之前ticks耗尽,则系统将从中断计算的继续中创建新的引擎,并使当前的引擎expire。complete和expire将在引擎调用的延续中调用。
24 |
25 | Scheme编程语言第12.11节给出了引擎的实现
26 |
27 | 请勿使用定时器(见 set-timer)中断引擎,因为引擎是定时器实现的。
28 |
29 | 下面这个例子是一个10ticks的引擎。
30 |
31 | (define
32 | (make-engine
33 | (lambda()3)))
34 |
35 | (eng 10
36 | (lambda(ticks value)value)
37 | (lambda(x)x)) => 3
38 | (define eng
39 | (make-engine
40 | (lambda () 3)))
41 |
42 | (eng 10
43 | list
44 | (lambda (x) x)) => (9 3)
45 | (define fibonacci
46 | (lambda (n)
47 | (let fib ([i n])
48 | (cond
49 | [(= i 0) 0]
50 | [(= i 1) 1]
51 | [else (+ (fib (- i 1))
52 | (fib (- i 2)))]))))
53 |
54 | (define eng
55 | (make-engine
56 | (lambda ()
57 | (fibonacci 10))))
58 |
59 | (eng 50
60 | list
61 | (lambda (new-eng)
62 | (set! eng new-eng)
63 | "expired")) => "expired"
64 |
65 | (eng 50
66 | list
67 | (lambda (new-eng)
68 | (set! eng new-eng)
69 | "expired")) => "expired"
70 |
71 | (eng 50
72 | list
73 | (lambda (new-eng)
74 | (set! eng new-eng)
75 | "expired")) => "expired"
76 |
77 | (eng 50
78 | list
79 | (lambda (new-eng)
80 | (set! eng new-eng)
81 | "expired")) => (21 55)
82 |
--------------------------------------------------------------------------------
/08.NumericOperations.md:
--------------------------------------------------------------------------------
1 | # Chapter 8. 数字操作
2 |
3 | 本章介绍了 Chez Scheme 对数字对象上的标准操作集的扩展。参见"The Scheme Programming Language, 第 4 版", 第 6 章,或 R6RS 中对数字对象上标准操作的介绍。
4 |
5 | Chez Scheme 支持全部 Scheme 数字类型,包括精确和不精确整数,有理数,实数,及复数。并使用了一系列表示形式支持这些数据类型:
6 |
7 | Fixnums
8 | 在 fixnum 范围内表示精确整数(参见 `most-negative-fixnum` 和 `most-positive-fixnum` )。字符串,向量或 fxvector 的长度均被限定为 fixnum.
9 |
10 | Bignums
11 | 表示 fixnum 范围外的任意精度的精确整数。
12 |
13 | Ratnums
14 | 表示任意精度的精确有理数。每个有理数包含一个精确整数分子(fixnum 或 bignum)和一个精确整数分母。分数总是被约分为最简形式,分母从不为 1,而分子永不会为 0.
15 |
16 | Flonums
17 | 表示不精确的实数。Flonums 是 IEEE 64 位浮点数。(由于 flonums 不能表示无理数,所有不精确的实数实际只是指有理数,虽然它们可能近似于无理数的数值)。
18 |
19 | Exact complexnums
20 | 表示精确的复数。每个复数包含一个精确有理数(fixnum, bignum, 或 ratnum)的实部,和一个精确有理数的虚部。
21 |
22 | Inexact complexnums
23 | 表示不精确的复数。每个不精确的复数包含一个浮点数实部和一个浮点数虚部。
24 |
25 | 大多数数字只能以一种方式表示;不过,实数有时会表示为虚部为 0 的不精确复数。
26 |
27 | Chez Scheme 对数字对象的语法进行了扩展,包括 2 至 36 的任意基数,非十进制的浮点数,科学表示法,以及 IEEE 无穷数和 NAN(NAN 是 "not-a-number" 的缩写)的打印形式。
28 |
29 | 任意基数由前缀 `#nr` 指定, `n` 的范围是 2 至 36. 大于 9 的数字用字母 `a` 至 `z` 指定(大写小写均可)。例如, ~#2r101 ~ 是 510, 而 ~#36rZ ~ 是 3510.
30 |
31 | 对于较大的基数,对于特定字母的解读会出现歧义,例如, `e` , 作为数字还是作为指数指示符;在这类情况下,字母被当作一个数字。例如,在 `#x3.2e5` 中, `e` 被解读为一个数字,而不作为指数标记。而在 `3.2e5` 中, `e` 则被当作指数标记。
32 |
33 | IEEE 无穷数被打印为+inf.0 和-inf.0, 而 IEEE NANs 被打印为 +nan.0 或-nan.0. (+nan.0 被用作所有 NANs 的输出)
34 |
35 | (/ 1.0 0.0) => +inf.0
36 | (/ 1.0 -0.0) => -inf.0
37 | (/ 0.0 0.0) => +nan.0
38 | (/ +inf.0 -inf.0) => +nan.0
39 |
40 | 本章第 1 节介绍了特定类型的数字类型谓词。8.2 至 8.4 节介绍了应用于 fixnums, flonums, 和不精确的复数(flonums 和/或不精确的 complexnums)上的快速的,类型专用的数字操作。专用于 fixnum 的版本,应只用于程序员确定操作数和结果都会是 fixnum 时,即,整数在范围 `(most-negative-fixnum)` 至 `(most-positive-fixnum)` 之间时(两端均包含)。专用于 flonum 的版本,应只用于输入和输出确定为 flonum 时。flonum/complexnum 混合版本,应只用于输入确定为 flonum 或不精确的 complexnum 时。8.5 节介绍了支持把精确整数作为比特集合或序列的,任意精度和专用于 fixnum 的操作。8.6 节介绍了随机数生成,8.7 节介绍了其余各种数字操作。
41 |
42 |
43 | ## Section 8.1. 数字类型谓词
44 |
45 | R6RS 区分了两种特殊的数字对象:fixnum 和 flonum. Chez Scheme 进一步区分了 bignum(fixnum 范围之外的精确整数)和 ratnum(精确整数的比值)。它同时也提供了一个识别 cflonum 的谓词,其为 flonum 或不精确的复数。
46 |
47 | procedure: (bignum? obj)
48 |
49 | 返回: 如果 `obj` 是 bignum,则为 `#t`, 否则为 `#f`.
50 |
51 | libraries: (chezscheme)
52 |
53 | (bignum? 0) => #f
54 | (bignum? (most-positive-fixnum)) => #f
55 | (bignum? (most-negative-fixnum)) => #f
56 | (bignum? (* (most-positive-fixnum) 2)) => #t
57 | (bignum? 3/4) => #f
58 | (bignum? 'a) => #f
59 |
60 | procedure: (ratnum? obj)
61 |
62 | 返回: 如果 `obj` 是 ratnum,则为 `#t`, 否则为 `#f`.
63 |
64 | libraries: (chezscheme)
65 |
66 | (ratnum? 0) => #f
67 | (ratnum? (* (most-positive-fixnum) 2)) => #f
68 | (ratnum? 3/4) => #t
69 | (ratnum? -10/2) => #f
70 | (ratnum? -11/2) => #t
71 | (ratnum? 'a) => #f
72 |
73 | procedure: (cflonum? obj)
74 |
75 | 返回: 如果 `obj` 是不精确的 complexnum 或 flonum,则为 `#t`, 否则为 `#f`.
76 |
77 | libraries: (chezscheme)
78 |
79 | (cflonum? 0) => #f
80 | (cflonum? 0.0) => #t
81 | (cflonum? 3+4i) => #f
82 | (cflonum? 3.0+4i) => #t
83 | (cflonum? +i) => #f
84 | (cflonum? +1.0i) => #t
85 |
86 |
87 | ## Section 8.2. Fixnum 操作
88 |
89 | 专用于 fixnum 的过程,通过会检查它们的输入和输出(需要时),但编译器在优化级别 3 下生成的代码,大多数情况下不执行这些检查。
90 |
91 | procedure: (most-positive-fixnum)
92 |
93 | returns: 系统支持的最大正 fixnum
94 |
95 | procedure: (most-negative-fixnum)
96 |
97 | returns: 系统支持的最大负 fixnum
98 |
99 | libraries: (chezscheme)
100 |
101 | 这些过程和 R6RS 中的 `greatest-fixnum` 和 `least-fixnum` 过程一样。
102 |
103 | procedure: (fx= fixnum1 fixnum2 …)
104 |
105 | procedure: (fx< fixnum1 fixnum2 …)
106 |
107 | procedure: (fx> fixnum1 fixnum2 …)
108 |
109 | procedure: (fx<= fixnum1 fixnum2 …)
110 |
111 | procedure: (fx>= fixnum1 fixnum2 …)
112 |
113 | 返回: 如果关系成立,则为 `#t`, 否则为 `#f`.
114 |
115 | libraries: (chezscheme)
116 |
117 | 谓词 fx=在其实参相等时返回#t。谓词 fx<在其实参单调递增时返回#t,即,每个实参都大于它前一个实参,而 fx>在其实参单调递减时返回#t。谓词 fx<=在其实参单调非递减时返回#t,即,每个实参不小于它前一个实参,而 fx>=在其实参单调非递增时返回#t。只传入一个实参时,这些谓词均返回#t。
118 |
119 | 这些过程与 R6RS 中的过程 fx=?, fx, fx>?, fx<=?, 和 fx>=?类似,除了 R6RS 中的过程需要 2 个或更多参数,以及它们的名称中都有 "?" 后缀以外。
120 |
121 | (fx= 0) => #t
122 | (fx= 0 0) => #t
123 | (fx< (most-negative-fixnum) 0 (most-positive-fixnum)) => #t
124 | (let ([x 3]) (fx<= 0 x 9)) => #t
125 | (fx<= 0 3 3) => #t
126 | (fx>= 0 0 (most-negative-fixnum)) => #t
127 |
128 | procedure: (fxnonpositive? fixnum)
129 |
130 | 返回: 如果 fixnum 不大于 0,则为 `#t`, 否则为 `#f`.
131 |
132 | procedure: (fxnonnegative? fixnum)
133 |
134 | 返回: 如果 fixnum 不小于 0,则为 `#t`, 否则为 `#f`.
135 |
136 | libraries: (chezscheme)
137 |
138 | fxnonpositive?等价于(lambda (x) (fx<= x 0)), 而 fxnonnegative?等价于(lambda (x) (fx>= x 0)).
139 |
140 | (fxnonpositive? 128) => #f
141 | (fxnonpositive? 0) => #t
142 | (fxnonpositive? -1) => #t
143 |
144 | (fxnonnegative? -65) => #f
145 | (fxnonnegative? 0) => #t
146 | (fxnonnegative? 1) => #t
147 |
148 | procedure: (fx+ fixnum …)
149 |
150 | returns: 实参 fixnum …之和
151 |
152 | libraries: (chezscheme)
153 |
154 | When called with no arguments, fx+ returns 0.
155 | 不带参数调用时,fx+返回 0.
156 |
157 | (fx+) => 0
158 | (fx+ 1 2) => 3
159 | (fx+ 3 4 5) => 12
160 | (apply fx+ '(1 2 3 4 5)) => 15
161 |
162 | procedure: (fx- fixnum1 fixnum2 …)
163 |
164 | returns: 一个 fixnum
165 |
166 | libraries: (chezscheme)
167 |
168 | 调用时若只传入一个实参,fx-返回 fixnum1 的负值。即,(fx- fixnum1)相当于(fx- 0 fixnum1).
169 |
170 | 调用时若传入 2 个以上的实参,fx-返回从 fixnum1 中减去数字 fixnum2 …之和的结果。
171 |
172 | (fx- 3) => -3
173 | (fx- 4 3) => 1
174 | (fx- 4 3 2 1) => -2
175 |
176 | procedure: (fx\* fixnum …)
177 |
178 | returns: 参数 fixnum …的乘积
179 |
180 | libraries: (chezscheme)
181 |
182 | 不带参数调用时,fx\*返回 1.
183 |
184 | (fx*) => 1
185 | (fx* 1 2) => 2
186 | (fx* 3 -4 5) => -60
187 | (apply fx* '(1 -2 3 -4 5)) => 120
188 |
189 | procedure: (fx/ fixnum1 fixnum2 …)
190 |
191 | returns: 参见后面的解释
192 |
193 | libraries: (chezscheme)
194 |
195 | 调用时若只传入一个实参,fx/返回 fixnum1 的倒数。即,(fx/ fixnum1)相当于(fx/ 1 fixnum1).
196 |
197 | 调用时若传入 2 个以上的实参,fx/ 返回 fixnum1 除以其余实参 fixnum2 …的乘积的结果。
198 |
199 | (fx/ 1) => 1
200 | (fx/ -17) => 0
201 | (fx/ 8 -2) => -4
202 | (fx/ -9 2) => -4
203 | (fx/ 60 5 3 2) => 2
204 |
205 | procedure: (fx1+ fixnum)
206 |
207 | procedure: (fx1- fixnum)
208 |
209 | returns: fixnum 加 1 或 fixnum 减 1
210 |
211 | libraries: (chezscheme)
212 |
213 | (define fxplus
214 | (lambda (x y)
215 | (if (fxzero? x)
216 | y
217 | (fxplus (fx1- x) (fx1+ y)))))
218 |
219 | (fxplus 7 8) => 15
220 |
221 | fx1+ 和 fx1- 可依如下定义:
222 |
223 | (define fx1+ (lambda (x) (fx+ x 1)))
224 | (define fx1- (lambda (x) (fx- x 1)))
225 |
226 | procedure: (fxquotient fixnum1 fixnum2 …)
227 |
228 | returns: 参见后面的解释
229 |
230 | libraries: (chezscheme)
231 |
232 | fxquotient 和 fx/一样。参见上面关于 fx/的介绍。
233 |
234 | procedure: (fxremainder fixnum1 fixnum2)
235 |
236 | returns: fixnum1 除以 fixnum2 的 fixnum 余数
237 |
238 | libraries: (chezscheme)
239 |
240 | fxremainder 结果的符号与 fixnum1 相同。
241 |
242 | (fxremainder 16 4) => 0
243 | (fxremainder 5 2) => 1
244 | (fxremainder -45 7) => -3
245 | (fxremainder 10 -3) => 1
246 | (fxremainder -17 -9) => -8
247 |
248 | procedure: (fxmodulo fixnum1 fixnum2)
249 |
250 | returns: fixnum1 和 fixnum2 的 fixnum 模数
251 |
252 | libraries: (chezscheme)
253 |
254 | fxmodulo 结果的符号与 fixnum2 相同。
255 |
256 | (fxmodulo 16 4) => 0
257 | (fxmodulo 5 2) => 1
258 | (fxmodulo -45 7) => 4
259 | (fxmodulo 10 -3) => -2
260 | (fxmodulo -17 -9) => -8
261 |
262 | procedure: (fxabs fixnum)
263 |
264 | returns: fixnum 的绝对值
265 |
266 | libraries: (chezscheme)
267 |
268 | (fxabs 1) => 1
269 | (fxabs -1) => 1
270 | (fxabs 0) => 0
271 |
272 |
273 | ## Section 8.3. Flonum 操作
274 |
275 | 不精确的实数通常以 flonum 表示。flonum 是一个单一的 64 位双精度浮点数。本节介绍针对 flonum 的操作,其中大多数接受 flonum 类型的实参,并返回 flonum 类型的值。大多数情况下,这些操作是内联编码或是在不进行实参类型检查的优化级别 3 下编码为机器语言子程序;全面的类型检查是在更低的优化级别下执行的。专用于 flonum 的过程名称带有前缀"fl",以区分于它们的通用版本。
276 |
277 | 不精确的实数也可以用虚部为 0 的不精确 complexnums 表示,这种表示形式不可用于 flonum 专用操作符的输入。然而,这些数字只会在显式调用 fl-make-rectangular, make-rectangular, 或 make-polar 时,或通过极坐标或直角坐标形式的数字输入,由涉及虚部非 0 的复数操作中生成。
278 |
279 | procedure: (flonum->fixnum flonum)
280 |
281 | returns: flonum 的 fixnum 表示形式(被截短的)
282 |
283 | libraries: (chezscheme)
284 |
285 | flonum 被截短后的值必须落在 fixnum 范围内。flonum->fixnum 是一个精确值的限定版本,会把任何数字表示转化为它的精确等价版本。
286 |
287 | (flonum->fixnum 0.0) => 0
288 | (flonum->fixnum 3.9) => 3
289 | (flonum->fixnum -2.2) => -2
290 |
291 | procedure: (fl= flonum1 flonum2 …)
292 |
293 | procedure: (fl< flonum1 flonum2 …)
294 |
295 | procedure: (fl> flonum1 flonum2 …)
296 |
297 | procedure: (fl<= flonum1 flonum2 …)
298 |
299 | procedure: (fl>= flonum1 flonum2 …)
300 |
301 | 返回: 如果关系成立,则为 `#t`, 否则为 `#f`.
302 |
303 | libraries: (chezscheme)
304 |
305 | 谓词 fl=在其实参相等时返回#t。谓词 fl<在其实参单调递增时返回#t,即,每个实参都大于它前一个实参,而 fl>在其实参单调递减时返回#t。谓词 fl<=在其实参单调非递减时返回#t,即,每个实参不小于它前一个实参,而 fl>=在其实参单调非递增时返回#t。只传入一个实参时,这些谓词均返回#t。
306 |
307 | IEEE NANs 不具有可比较性,即,涉及 NANs 的比较总是返回#f.
308 |
309 | 这些过程与 R6RS 中的过程 fl=?, fl, fl>?, fl<=?, 和 fl>=?类似,除了 R6RS 中的过程需要 2 个或更多参数,以及它们的名称中都有 "?" 后缀以外。
310 |
311 | (fl= 0.0) => #t
312 | (fl= 0.0 0.0) => #t
313 | (fl< -1.0 0.0 1.0) => #t
314 | (fl> -1.0 0.0 1.0) => #f
315 | (fl<= 0.0 3.0 3.0) => #t
316 | (fl>= 4.0 3.0 3.0) => #t
317 | (fl< 7.0 +inf.0) => #t
318 | (fl= +nan.0 0.0) => #f
319 | (fl= +nan.0 +nan.0) => #f
320 | (fl< +nan.0 +nan.0) => #f
321 | (fl> +nan.0 +nan.0) => #f
322 |
323 | procedure: (flnonpositive? fl)
324 |
325 | 返回: 如果 fl 不大于 0,则为 `#t`, 否则为 `#f`.
326 |
327 | procedure: (flnonnegative? fl)
328 |
329 | 返回: 如果 fl 不小于 0,则为 `#t`, 否则为 `#f`.
330 |
331 | libraries: (chezscheme)
332 |
333 | flnonpositive?等价于(lambda (x) (fl<= x 0.0)), 而 flnonnegative?等价于(lambda (x) (fl>= x 0.0)).
334 |
335 | 虽然 flonum 的表示形式区分-0.0 和+0.0,但都判定为非负且非正。
336 |
337 | (flnonpositive? 128.0) => #f
338 | (flnonpositive? 0.0) => #t
339 | (flnonpositive? -0.0) => #t
340 | (flnonpositive? -1.0) => #t
341 |
342 | (flnonnegative? -65.0) => #f
343 | (flnonnegative? 0.0) => #t
344 | (flnonnegative? -0.0) => #t
345 | (flnonnegative? 1.0) => #t
346 |
347 | (flnonnegative? +nan.0) => #f
348 | (flnonpositive? +nan.0) => #f
349 |
350 | (flnonnegative? +inf.0) => #t
351 | (flnonnegative? -inf.0) => #f
352 |
353 | procedure: (decode-float x)
354 |
355 | returns: 参见下文
356 |
357 | libraries: (chezscheme)
358 |
359 | x 必须是 flonum. decode-float 返回一个包含 3 个整数元素的向量,m, e, 和 s, 满足 x = sm2e. 它主要是用于打印浮点数。
360 |
361 | (decode-float 1.0) => #(4503599627370496 -52 1)
362 | (decode-float -1.0) => #(4503599627370496 -52 -1)
363 |
364 | (define slow-identity
365 | (lambda (x)
366 | (inexact
367 | (let ([v (decode-float x)])
368 | (let ([m (vector-ref v 0)]
369 | [e (vector-ref v 1)]
370 | [s (vector-ref v 2)])
371 | (* s m (expt 2 e)))))))
372 |
373 | (slow-identity 1.0) => 1.0
374 | (slow-identity -1e20) => -1e20
375 |
376 | procedure: (fllp flonum)
377 |
378 | returns: 参见下文
379 |
380 | libraries: (chezscheme)
381 |
382 | fllp 返回一个 12 位整数,由指数加上一个 flonum(ieee 64 位浮点数)的最高表示位组成。它可以用于快速计算这个数的对数的近似值。
383 |
384 | (fllp 0.0) => 0
385 | (fllp 1.0) => 2046
386 | (fllp -1.0) => 2046
387 |
388 | (fllp 1.5) => 2047
389 |
390 | (fllp +inf.0) => 4094
391 | (fllp -inf.0) => 4094
392 |
393 | (fllp #b1.0e-1111111111) => 1
394 | (fllp #b1.0e-10000000000) => 0
395 |
396 |
397 | ## Section 8.4. 不精确复数的操作
398 |
399 | 本节介绍的过程提供了创建和操作不精确复数的机制。虚部非 0 的不精确复数表示为不精确的 complexnums. 不精确的 complexnum 包含 2 个 64 位双精度浮点数。虚部为 0 的不精确复数(即, 不精确实数)可以表示为不精确 complexnums 或 flonums. 本节介绍的操作接受任何不精确 complexnum 和 flonum(合称为"cflonums")实参的组合。
400 |
401 | 大多数情况下,这些操作在优化级别 3 执行最少化的类型检查;在更低的优化级别才会执行全面的类型检查。不精确复数过程的名称以前缀 "cfl" 开始,以区分于它们对应的通用版本。
402 |
403 | procedure: (fl-make-rectangular flonum1 flonum2)
404 |
405 | returns: 一个不精确 complexnum
406 |
407 | libraries: (chezscheme)
408 |
409 | 通过 fl-make-rectangular 生成的不精确 complexnum,实部等于 flonum1, 而虚部等于 flonum2.
410 |
411 | (fl-make-rectangular 2.0 -3.0) => 2.0-3.0i
412 | (fl-make-rectangular 2.0 0.0) => 2.0+0.0i
413 | (fl-make-rectangular 2.0 -0.0) => 2.0-0.0i
414 |
415 | procedure: (cfl-real-part cflonum)
416 |
417 | returns: cflonum 的实部
418 |
419 | procedure: (cfl-imag-part cflonum)
420 |
421 | returns: cflonum 的虚部
422 |
423 | libraries: (chezscheme)
424 |
425 | (cfl-real-part 2.0-3.0i) => 2.0
426 | (cfl-imag-part 2.0-3.0i) => -3.0
427 | (cfl-imag-part 2.0-0.0i) => -0.0
428 | (cfl-imag-part 2.0-inf.0i) => -inf.0
429 |
430 | procedure: (cfl= cflonum …)
431 |
432 | 返回: 如果实参相等,则为 `#t`, 否则为 `#f`.
433 |
434 | libraries: (chezscheme)
435 |
436 | (cfl= 7.0+0.0i 7.0) => #t
437 | (cfl= 1.0+2.0i 1.0+2.0i) => #t
438 | (cfl= 1.0+2.0i 1.0-2.0i) => #f
439 |
440 | procedure: (cfl+ cflonum …)
441 |
442 | procedure: (cfl\* cflonum …)
443 |
444 | procedure: (cfl- cflonum1 cflonum2 …)
445 |
446 | procedure: (cfl/ cflonum1 cflonum2 …)
447 |
448 | returns: 一个 cflonum
449 |
450 | libraries: (chezscheme)
451 |
452 | 这些过程计算不精确复数数值的和,差,积或商,不论这些数值是以 flonums 表示,或以不精确 complexnums 表示。例如,如果 cfl+收到 2 个 flonum 实参 a 和 b,它返回它们的和 a + b; 在这种情况下,它和 fl+ 的行为一样。若 2 个实参为不精确 complexnum a + bi 和 c + di, 它返回和 (a + c) + (b + d)i. 若 1 个实参为 flonum a,而另一个为不精确 complexnum c + di, cfl+ 返回 (a + c) + di.
453 |
454 | 当传入 0 个实参,cfl+ 返回 0.0,而 cfl\* 返回 1.0. 当传入 1 个实参,cfl- 返回实参的加法逆元,而 cfl/ 返回实参的乘法逆元。当传入 3 个或更多实参,cfl- 返回其第一个实参与其余实参之和的差,而 cfl/ 返回其第一个实参与其余实参之积的商。
455 |
456 | (cfl+) => 0.0
457 | (cfl*) => 1.0
458 | (cfl- 5.0+1.0i) => -5.0-1.0i
459 | (cfl/ 2.0+2.0i) => 0.25-0.25i
460 |
461 | (cfl+ 1.0+2.2i -3.7+5.3i) => -2.7+7.5i
462 | (cfl+ 1.0 -5.3) => -4.3
463 | (cfl+ 1.0 2.0 -5.3i) => 3.0-5.3i
464 | (cfl- 1.0+2.5i -3.7) => 4.7+2.5i
465 | (cfl* 1.0+2.0i 3.0+4.0i) => -5.0+10.0i
466 | (cfl/ -5.0+10.0i 1.0+2.0i 2.0) => 1.5+2.0i
467 |
468 | procedure: (cfl-conjugate cflonum)
469 |
470 | returns: cflonum 的共轭复数
471 |
472 | libraries: (chezscheme)
473 |
474 | 当传入一个不精确的复数实参 a + bi 时,过程 cfl-conjugate 返回它的共轭复数 a + (-b)i.
475 |
476 | 参见 conjugate, 其为这个操作符的通用版本,对于一个复数的任何有效表现形式,返回其共轭复数。
477 |
478 | (cfl-conjugate 3.0) => 3.0
479 | (cfl-conjugate 3.0+4.0i) => 3.0-4.0i
480 | (cfl-conjugate 1e-20-2e-30i) => 1e-20+2e-30i
481 |
482 | procedure: (cfl-magnitude-squared cflonum)
483 |
484 | returns: cflonum 的模的平方
485 |
486 | libraries: (chezscheme)
487 |
488 | 当传入 1 个不精确的复数实参 a + bi, 过程 cfl-magnitude-squared 返回一个表示此实参的模的平方的 flonum, 即,a2 + b2.
489 |
490 | 参见 magnitude-squared, 其为此操作符的通用版本,对于 1 个复数的任何有效表示形式,返回它的模的平方。这两个操作都类似于过程 magnitude, 其返回它的复数实参的模,sqrt(a2 + b2).
491 |
492 | (cfl-magnitude-squared 3.0) => 9.0
493 | (cfl-magnitude-squared 3.0-4.0i) => 25.0
494 |
495 |
496 | ## Section 8.5. 位和逻辑操作符
497 |
498 | Chez Scheme 提供了一系列的逻辑操作符,以支持把精确整数 (fixnums 和 bignums) 作为位集合或位序列来处理。这些操作符包括 logand(逐位的逻辑与),logior(逐位的逻辑或),logxor(逐位的逻辑异或),lognot(逐位的逻辑非),logtest(测试多位),logbit?(测试单个位),logbit0(重置单个位),logbit1(设置单个位),以及 ash(算术移位)。这些操作符均把它的实参作为补码整数处理,不论这些实参的底层表示形式是什么。这种处理方法可被利用于表示无穷集合:a negative number represents an infinite number of one bits beyond the leftmost zero, and a nonnegative number represents an infinite number of zero bits beyond the leftmost one bit.
499 |
500 | 也提供了 fixnum 相应版本的逻辑操作符,比如 fxlogand, fxlogior, fxlogxor, fxlognot, fxlogtest, fxlogbit?, fxlogbit0, 以及 fxlogbit1. 还有三个用于移位的 fixnum 操作符: fxsll(逻辑左移位),fxsrl(逻辑右移位),fxsra(算术右移位)。逻辑和算术移位只在右移位上有差别。逻辑右移位在最左端填充 0,而算术右移位则复制符号位。
501 |
502 | 对任意精度的整数,逻辑移位没有意义,因为这些数没有必须移位的“最左端”。
503 |
504 | procedure: (logand int …)
505 |
506 | returns: 实参 int …的逻辑与
507 |
508 | libraries: (chezscheme)
509 |
510 | 实参必须是精确的整数(fixnums 或 bignums),而且被作为补码整数处理,无论其底层的表示形式是什么。不传入实参时,logand 返回-1,即,所有位都设为 1.
511 |
512 | (logand) => -1
513 | (logand 15) => 15
514 | (logand -1 -1) => -1
515 | (logand -1 0) => 0
516 | (logand 5 3) => 1
517 | (logand #x173C8D95 7) => 5
518 | (logand #x173C8D95 -8) => #x173C8D90
519 | (logand #b1100 #b1111 #b1101) => #b1100
520 |
521 | procedure: (logior int …)
522 |
523 | procedure: (logor int …)
524 |
525 | returns: 实参 int …的逻辑或
526 |
527 | libraries: (chezscheme)
528 |
529 | 实参必须是精确的整数(fixnums 或 bignums),而且被作为补码整数处理,无论其底层的表示形式是什么。不传入实参时,logior 返回 0,即,所有位都被重置。
530 |
531 | (logior) => 0
532 | (logior 15) => 15
533 | (logior -1 -1) => -1
534 | (logior -1 0) => -1
535 | (logior 5 3) => 7
536 | (logior #b111000 #b101010) => #b111010
537 | (logior #b1000 #b0100 #b0010) => #b1110
538 | (apply logior '(1 2 4 8 16)) => 31
539 |
540 | procedure: (logxor int …)
541 |
542 | returns: 实参 int …的逻辑异或
543 |
544 | libraries: (chezscheme)
545 |
546 | 实参必须是精确的整数(fixnums 或 bignums),而且被作为补码整数处理,无论其底层的表示形式是什么。不传入实参时,logxor 返回 0,即,所有位都被重置。
547 |
548 | (logxor) => 0
549 | (logxor 15) => 15
550 | (logxor -1 -1) => 0
551 | (logxor -1 0) => -1
552 | (logxor 5 3) => 6
553 | (logxor #b111000 #b101010) => #b010010
554 | (logxor #b1100 #b0100 #b0110) => #b1110
555 |
556 | procedure: (lognot int)
557 |
558 | returns: int 的逻辑非
559 |
560 | libraries: (chezscheme)
561 |
562 | 实参必须是精确的整数(fixnums 或 bignums),而且被作为补码整数处理,无论其底层的表示形式是什么。
563 |
564 | (lognot -1) => 0
565 | (lognot 0) => -1
566 | (lognot 7) => -8
567 | (lognot -8) => 7
568 |
569 | procedure: (logbit? index int)
570 |
571 | 返回: 如果指定位被设置为 1,则为 `#t`, 否则为 `#f`.
572 |
573 | libraries: (chezscheme)
574 |
575 | 实参必须是精确的整数(fixnums 或 bignums),而且被作为补码整数处理,无论其底层的表示形式是什么。
576 |
577 | 如果在 int 的索引 index 处的位被设为 1,logbit? 返回#t, 否则返回#f. 索引基于 0,从最低位向最高位数。索引没有上限;对于非负的 int,在最高的设置位以上的位均被当作 0,而对负值,在最高的重置位以上的位都被当作 1.
578 |
579 | logbit? 等价于
580 |
581 | (lambda (k n) (not (zero? (logand n (ash 1 k)))))
582 |
583 | 但更加高效。
584 |
585 | (logbit? 0 #b1110) => #f
586 | (logbit? 1 #b1110) => #t
587 | (logbit? 2 #b1110) => #t
588 | (logbit? 3 #b1110) => #t
589 | (logbit? 4 #b1110) => #f
590 | (logbit? 100 #b1110) => #f
591 |
592 | (logbit? 0 -6) => #f ; the two's complement of -6 is 1...1010
593 | (logbit? 1 -6) => #t
594 | (logbit? 2 -6) => #f
595 | (logbit? 3 -6) => #t
596 | (logbit? 100 -6) => #t
597 |
598 | (logbit? (random 1000000) 0) => #f
599 | (logbit? (random 1000000) -1) => #t
600 |
601 | (logbit? 20000 (ash 1 20000)) => #t
602 |
603 | procedure: (logtest int1 int2)
604 |
605 | 返回: 如果任何共同位被设为 1,则为 `#t`, 否则为 `#f`.
606 |
607 | libraries: (chezscheme)
608 |
609 | 实参必须是精确的整数(fixnums 或 bignums),而且被作为补码整数处理,无论其底层的表示形式是什么。
610 |
611 | 如果有任何位在两个实参中都被设为 1,则 logtest 返回#t. 如果两个实参中没有任何共同位被设为 1,则返回#f.
612 |
613 | logtest 等价于
614 |
615 | (lambda (n1 n2) (not (zero? (logand n1 n2))))
616 |
617 | 但更加高效。
618 |
619 | (logtest #b10001 #b1110) => #f
620 | (logtest #b10101 #b1110) => #t
621 | (logtest #b111000 #b110111) => #t
622 |
623 | (logtest #b101 -6) => #f ; the two's complement of -6 is 1...1010
624 | (logtest #b1000 -6) => #t
625 | (logtest 100 -6) => #t
626 |
627 | (logtest (+ (random 1000000) 1) 0) => #f
628 | (logtest (+ (random 1000000) 1) -1) => #t
629 |
630 | (logtest (ash #b101 20000) (ash #b111 20000)) => #t
631 |
632 | procedure: (logbit0 index int)
633 |
634 | returns: 把 int 在索引 index 处的位清 0 后的结果
635 |
636 | libraries: (chezscheme)
637 |
638 | index 必须是一个非负的精确整数。int 必须是一个精确整数(fixnum 或 bignum),且被作为补码整数处理,无论它底层的表示形式是什么。
639 |
640 | 索引基于 0,从低位到高位计数。和 logbit?一样,索引没有上限。
641 |
642 | logbit0 等价于
643 |
644 | (lambda (i n) (logand (lognot (ash 1 i)) n))
645 |
646 | 但更加高效。
647 |
648 | (logbit0 3 #b10101010) => #b10100010
649 | (logbit0 4 #b10101010) => #b10101010
650 | (logbit0 0 -1) => -2
651 |
652 | procedure: (logbit1 index int)
653 |
654 | returns: 把 int 在索引 index 处的位设为 1 后的结果
655 |
656 | libraries: (chezscheme)
657 |
658 | index 必须是一个非负的精确整数。int 必须是一个精确整数(fixnum 或 bignum),且被作为补码整数处理,无论它底层的表示形式是什么。
659 |
660 | 索引基于 0,从低位到高位计数。和 logbit?一样,索引没有上限。
661 |
662 | logbit1 等价于
663 |
664 | (lambda (i n) (logor (ash 1 i) n))
665 |
666 | 但更加高效。
667 |
668 | (logbit1 3 #b10101010) => #b10101010
669 | (logbit1 4 #b10101010) => #b10111010
670 | (logbit1 4 0) => #b10000
671 | (logbit1 0 -2) => -1
672 |
673 | procedure: (ash int count)
674 |
675 | returns: int 算术左移 count 位的结果
676 |
677 | libraries: (chezscheme)
678 |
679 | 两个实参都必须是精确整数。第一个实参被作为补码整数处理,无论其底层的表示形式是什么。如果 count 是负数,则 int 右移 -count 位。
680 |
681 | (ash 8 0) => 8
682 | (ash 8 2) => 32
683 | (ash 8 -2) => 2
684 | (ash -1 2) => -4
685 | (ash -1 -2) => -1
686 |
687 | procedure: (fxlogand fixnum …)
688 |
689 | returns: 实参 fixnum …的逻辑与
690 |
691 | libraries: (chezscheme)
692 |
693 | 实参均被作为补码整数处理,无论其底层的表示形式是什么。不传入实参时,fxlogand 返回 -1, 即,所有位都设为 1.
694 |
695 | (fxlogand) => -1
696 | (fxlogand 15) => 15
697 | (fxlogand -1 -1) => -1
698 | (fxlogand -1 0) => 0
699 | (fxlogand 5 3) => 1
700 | (fxlogand #b111000 #b101010) => #b101000
701 | (fxlogand #b1100 #b1111 #b1101) => #b1100
702 |
703 | procedure: (fxlogior fixnum …)
704 |
705 | procedure: (fxlogor fixnum …)
706 |
707 | returns: 实参 fixnum …的逻辑或
708 |
709 | libraries: (chezscheme)
710 |
711 | 实参均被作为补码整数处理,无论其底层的表示形式是什么。不传入实参时,fxlogior 返回 0, 即,所有位都被重置。
712 |
713 | (fxlogior) => 0
714 | (fxlogior 15) => 15
715 | (fxlogior -1 -1) => -1
716 | (fxlogior -1 0) => -1
717 | (fxlogior #b111000 #b101010) => #b111010
718 | (fxlogior #b1000 #b0100 #b0010) => #b1110
719 | (apply fxlogior '(1 2 4 8 16)) => 31
720 |
721 | procedure: (fxlogxor fixnum …)
722 |
723 | returns: 实参 fixnum …的逻辑异或
724 |
725 | libraries: (chezscheme)
726 |
727 | The arguments are treated as two's complement integers, regardless of the underlying representation. With no arguments, fxlogxor returns 0, i.e., all bits reset.
728 | 实参均被作为补码整数处理,无论其底层的表示形式是什么。不传入实参时,fxlogxor 返回 0, 即,所有位都被重置。
729 |
730 | (fxlogxor) => 0
731 | (fxlogxor 15) => 15
732 | (fxlogxor -1 -1) => 0
733 | (fxlogxor -1 0) => -1
734 | (fxlogxor 5 3) => 6
735 | (fxlogxor #b111000 #b101010) => #b010010
736 | (fxlogxor #b1100 #b0100 #b0110) => #b1110
737 |
738 | procedure: (fxlognot fixnum)
739 |
740 | returns: fixnum 的逻辑非
741 |
742 | libraries: (chezscheme)
743 |
744 | 实参被作为补码整数处理,无论其底层的表示形式是什么。
745 |
746 | (fxlognot -1) => 0
747 | (fxlognot 0) => -1
748 | (fxlognot 1) => -2
749 | (fxlognot -2) => 1
750 |
751 | procedure: (fxlogbit? index fixnum)
752 |
753 | 返回: 如果指定位是 1, 则为 `#t`, 否则为 `#f`.
754 |
755 | libraries: (chezscheme)
756 |
757 | index 必须是非负 fixnum. fixnum 被作为补码整数处理,无论其底层的表示形式是什么。
758 |
759 | 如果 fixnum 在索引 index 处的位被设为 1,则 fxlogbit? 返回 #t, 否则返回#f. 索引基于 0,从最低位向最高位计数。索引只受限 fixnum 的范围;对于非负的 int,在最高的设置位以上的位均被当作 0,而对负值,在最高的重置位以上的位都被当作 1.
760 |
761 | (fxlogbit? 0 #b1110) => #f
762 | (fxlogbit? 1 #b1110) => #t
763 | (fxlogbit? 2 #b1110) => #t
764 | (fxlogbit? 3 #b1110) => #t
765 | (fxlogbit? 4 #b1110) => #f
766 | (fxlogbit? 100 #b1110) => #f
767 |
768 | (fxlogbit? 0 -6) => #f ; the two's complement of -6 is 1...1010
769 | (fxlogbit? 1 -6) => #t
770 | (fxlogbit? 2 -6) => #f
771 | (fxlogbit? 3 -6) => #t
772 | (fxlogbit? 100 -6) => #t
773 |
774 | (fxlogbit? (random 1000000) 0) => #f
775 | (fxlogbit? (random 1000000) -1) => #t
776 |
777 | procedure: (fxlogtest fixnum1 fixnum2)
778 |
779 | 返回: 如果任何共同位为 1,则为 `#t`, 否则为 `#f`.
780 |
781 | libraries: (chezscheme)
782 |
783 | 实参均被作为补码整数处理,无论其底层的表示形式是什么。
784 |
785 | 如果有任何位在两个实参中均为 1,则 fxlogtest 返回 #t. 如果两个实参中没有任何共同位被设为 1,则返回 #f.
786 |
787 | (fxlogtest #b10001 #b1110) => #f
788 | (fxlogtest #b10101 #b1110) => #t
789 | (fxlogtest #b111000 #b110111) => #t
790 |
791 | (fxlogtest #b101 -6) => #f ; the two's complement of -6 is 1...1010
792 | (fxlogtest #b1000 -6) => #t
793 | (fxlogtest 100 -6) => #t
794 |
795 | (fxlogtest (+ (random 1000000) 1) 0) => #f
796 | (fxlogtest (+ (random 1000000) 1) -1) => #t
797 |
798 | procedure: (fxlogbit0 index fixnum)
799 |
800 | returns: fixnum 在索引 index 处的位被清零后的结果
801 |
802 | libraries: (chezscheme)
803 |
804 | fixnum 被作为补码整数处理,无论其底层的表示形式是什么。index 必须非负,且小于一个 fixnum 中的位数(不包括符号位),即,小于 (integer-length (most-positive-fixnum)).索引基于 0,从最低位向最高位计数。
805 |
806 | fxlogbit0 等价于
807 |
808 | (lambda (i n) (fxlogand (fxlognot (fxsll 1 i)) n))
809 |
810 | 但更加高效。
811 |
812 | (fxlogbit0 3 #b10101010) => #b10100010
813 | (fxlogbit0 4 #b10101010) => #b10101010
814 | (fxlogbit0 0 -1) => -2
815 |
816 | procedure: (fxlogbit1 index fixnum)
817 |
818 | returns: fixnum 在索引 index 处的位被设为 1 后的结果
819 |
820 | libraries: (chezscheme)
821 |
822 | fixnum 被作为补码整数处理,无论其底层的表示形式是什么。index 必须非负,且小于一个 fixnum 中的位数(不包括符号位),即,小于 (integer-length (most-positive-fixnum)).索引基于 0,从最低位向最高位计数。
823 |
824 | fxlogbit1 等价于
825 |
826 | (lambda (i n) (fxlogor (fxsll 1 i) n))
827 |
828 | 但更加高效。
829 |
830 | (fxlogbit1 3 #b10101010) => #b10101010
831 | (fxlogbit1 4 #b10101010) => #b10111010
832 | (fxlogbit1 4 0) => #b10000
833 | (fxlogbit1 0 -2) => -1
834 |
835 | procedure: (fxsll fixnum count)
836 |
837 | returns: fixnum 左移 count 位
838 |
839 | libraries: (chezscheme)
840 |
841 | fixnum 被作为补码整数处理,无论其底层的表示形式是什么。count 必须非负,且不大于一个 fixnum 中的位数,即,(+ (integer-length (most-positive-fixnum)) 1). 如果结果不能表示为 fixnum,则会抛出一个条件类型的 &implementation-restriction 异常。
842 |
843 | (fxsll 1 2) => 4
844 | (fxsll -1 2) => -4
845 |
846 | procedure: (fxsrl fixnum count)
847 |
848 | returns: fixnum 逻辑右移 count 位
849 |
850 | libraries: (chezscheme)
851 |
852 | fixnum 被作为补码整数处理,无论其底层的表示形式是什么。count 必须非负,且不大于一个 fixnum 中的位数,即,(+ (integer-length (most-positive-fixnum)) 1).
853 |
854 | (fxsrl 4 2) => 1
855 | (= (fxsrl -1 1) (most-positive-fixnum)) => #t
856 |
857 | procedure: (fxsra fixnum count)
858 |
859 | returns: fixnum 算术右移 count 位
860 |
861 | libraries: (chezscheme)
862 |
863 | fixnum 被作为补码整数处理,无论其底层的表示形式是什么。count 必须非负,且不大于一个 fixnum 中的位数,即,(+ (integer-length (most-positive-fixnum)) 1).
864 |
865 | (fxsra 64 3) => 8
866 | (fxsra -1 1) => -1
867 | (fxsra -64 3) => -8
868 |
869 |
870 | ## Section 8.6. 随机数生成
871 |
872 | procedure: (random real)
873 |
874 | returns: 小于 real 的一个非负伪随机数
875 |
876 | libraries: (chezscheme)
877 |
878 | real 必须是正整数,或正的不精确实数。
879 |
880 | (random 1) => 0
881 | (random 1029384535235) => 1029384535001, every now and then
882 | (random 1.0) => 0.5, every now and then
883 |
884 | thread parameter: random-seed
885 |
886 | libraries: (chezscheme)
887 |
888 | 随机数生成器支持通过参数对象 random-seed 获取和修改当前的随机数种子。
889 |
890 | 不带实参调用时,random-seed 返回当前的随机数种子。当带 1 个实参调用时,实参必须是 1 至 232 -1 之间的非负精确整数,而 random-seed 把当前的随机数种子设为此实参的值。
891 |
892 | (let ([s (random-seed)])
893 | (let ([r1 (random 1.0)])
894 | (random-seed s)
895 | (eqv? (random 1.0) r1))) => #t
896 |
897 |
898 | ## Section 8.7. 其它各类数字操作
899 |
900 | procedure: (= num1 num2 num3 …)
901 |
902 | procedure: (< real1 real2 real3 …)
903 |
904 | procedure: (> real1 real2 real3 …)
905 |
906 | procedure: (<= real1 real2 real3 …)
907 |
908 | procedure: (>= real1 real2 real3 …)
909 |
910 | 返回: 如果关系成立,则为 `#t`, 否则为 `#f`.
911 |
912 | libraries: (chezscheme)
913 |
914 | 这些谓词和 R6RS 中的对应版本一样,只是它们被扩展为可接受 1 个以上参数,而非 2 个以上参数。当传入 1 个参数时,这些谓词均返回 #t.
915 |
916 | (> 3/4) => #t
917 | (< 3/4) => #t
918 | (= 3/4) => #t
919 |
920 | procedure: (1+ num)
921 |
922 | procedure: (add1 num)
923 |
924 | procedure: (1- num)
925 |
926 | procedure: (-1+ num)
927 |
928 | procedure: (sub1 num)
929 |
930 | returns: num 加 1 或 num 减 1
931 |
932 | libraries: (chezscheme)
933 |
934 | 1+ 和 add1 等价于 (lambda (x) (+ x 1)); 1-, -1+, 和 sub1 等价于 (lambda (x) (- x 1)).
935 |
936 | (define plus
937 | ; x should be a nonnegative integer
938 | (lambda (x y)
939 | (if (zero? x)
940 | y
941 | (plus (1- x) (1+ y)))))
942 |
943 | (plus 7 8) => 15
944 |
945 | (define double
946 | ; x should be a nonnegative integer
947 | (lambda (x)
948 | (if (zero? x)
949 | 0
950 | (add1 (add1 (double (sub1 x)))))))
951 |
952 | (double 7) => 14
953 |
954 | procedure: (expt-mod int1 int2 int3)
955 |
956 | returns: int1 的 int2 次方,再对 int3 取模
957 |
958 | libraries: (chezscheme)
959 |
960 | int1, int2 和 int3 必须是非负整数。expt-mod 采用的计算方式使得中间结果永远不会比 int3 大太多。这意味着,当 int2 很大时,expt-mod 要比等价过程 (lambda (x y z) (modulo (expt x y) z)) 更加高效。
961 |
962 | (expt-mod 2 4 3) => 1
963 | (expt-mod 2 76543 76543) => 2
964 |
965 | procedure: (isqrt n)
966 |
967 | returns: n 的整数平方根
968 |
969 | libraries: (chezscheme)
970 |
971 | n 必须是非负整数。The integer square root of n is defined to be =>.TODO
972 |
973 | (isqrt 0) => 0
974 | (isqrt 16) => 4
975 | (isqrt 16.0) => 4.0
976 | (isqrt 20) => 4
977 | (isqrt 20.0) => 4.0
978 | (isqrt (* 2 (expt 10 20))) => 14142135623
979 |
980 | procedure: (integer-length n)
981 |
982 | returns: 参见下文
983 |
984 | libraries: (chezscheme)
985 |
986 | 过程 integer-length 返回 n 的最小补码表示形式的位数长度,并假设负数前面有一个为 1 的符号位。对于 0,integer-length 返回 0.
987 |
988 | (integer-length 0) => 0
989 | (integer-length 1) => 1
990 | (integer-length 2) => 2
991 | (integer-length 3) => 2
992 | (integer-length 4) => 3
993 | (integer-length #b10000000) => 8
994 | (integer-length #b11111111) => 8
995 | (integer-length -1) => 0
996 | (integer-length -2) => 1
997 | (integer-length -3) => 2
998 | (integer-length -4) => 2
999 |
1000 | procedure: (nonpositive? real)
1001 |
1002 | 返回: 如果 real 不大于 0,则为 `#t`, 否则为 `#f`.
1003 |
1004 | libraries: (chezscheme)
1005 |
1006 | nonpositive? 等价于 (lambda (x) (<= x 0)).
1007 |
1008 | (nonpositive? 128) => #f
1009 | (nonpositive? 0.0) => #t
1010 | (nonpositive? 1.8e-15) => #f
1011 | (nonpositive? -2/3) => #t
1012 |
1013 | procedure: (nonnegative? real)
1014 |
1015 | 返回: 如果 real 不小于 0,则为 `#t`, 否则为 `#f`.
1016 |
1017 | libraries: (chezscheme)
1018 |
1019 | nonnegative? 等价于 (lambda (x) (>= x 0)).
1020 |
1021 | (nonnegative? -65) => #f
1022 | (nonnegative? 0) => #t
1023 | (nonnegative? -0.0121) => #f
1024 | (nonnegative? 15/16) => #t
1025 |
1026 | procedure: (conjugate num)
1027 |
1028 | returns: num 的共轭复数
1029 |
1030 | libraries: (chezscheme)
1031 |
1032 | 当过程 conjugate 传入复数实参 a + bi 时,返回它的共轭复数 a + (-b)i.
1033 |
1034 | (conjugate 3.0+4.0i) => 3.0-4.0i
1035 | (conjugate 1e-20-2e-30i) => 1e-20+2e-30i
1036 | (conjugate 3) => 3
1037 |
1038 | procedure: (magnitude-squared num)
1039 |
1040 | returns: num 的模的平方
1041 |
1042 | libraries: (chezscheme)
1043 |
1044 | 对过程 magnitude-squared 传入复数实参 a + bi 时,返回它的模的平方,即,a2 + b2.
1045 |
1046 | (magnitude-squared 3.0-4.0i) => 25.0
1047 | (magnitude-squared 3.0) => 9.0
1048 |
1049 | procedure: (sinh num)
1050 |
1051 | procedure: (cosh num)
1052 |
1053 | procedure: (tanh num)
1054 |
1055 | returns: num 的 hyperbolic sine, cosine, 或 tangent
1056 |
1057 | libraries: (chezscheme)
1058 |
1059 | (sinh 0.0) => 0.0
1060 | (cosh 0.0) => 1.0
1061 | (tanh -0.0) => -0.0
1062 |
1063 | procedure: (asinh num)
1064 |
1065 | procedure: (acosh num)
1066 |
1067 | procedure: (atanh num)
1068 |
1069 | returns: num 的 hyperbolic arc sine, arc cosine, 或 arc tangent
1070 |
1071 | libraries: (chezscheme)
1072 |
1073 | (acosh 0.0) => 0.0+1.5707963267948966i
1074 | (acosh 1.0) => 0.0
1075 | (atanh -1.0) => -inf.0
1076 |
1077 | procedure: (string->number string)
1078 |
1079 | procedure: (string->number string radix)
1080 |
1081 | returns: string 所表示的数字,或 #f
1082 |
1083 | libraries: (chezscheme)
1084 |
1085 | 这个过程和 R6RS 版本一样,除了基数可以是 2 至 36(两端包含)之间的任意精确整数。R6RS 版本要求基数属于集合 {2,8,10,16}.
1086 |
1087 | (string->number "211012" 3) => 559
1088 | (string->number "tobeornottobe" 36) => 140613689159812836698
1089 |
1090 | procedure: (number->string num)
1091 |
1092 | procedure: (number->string num radix)
1093 |
1094 | procedure: (number->string num radix precision)
1095 |
1096 | returns: 作为字符串的一种 num 的外部表示形式
1097 | an external representation of num as a string
1098 |
1099 | libraries: (chezscheme)
1100 |
1101 | 这个过程和 R6RS 版本一样,除了基数可以是 2 至 36(两端包含)之间的任意精确整数。R6RS 版本要求基数属于集合 {2,8,10,16}.
1102 |
1103 | (number->string 10000 4) => "2130100"
1104 | (number->string 10000 27) => "DJA"
1105 |
1106 | Chez Scheme Version 9 User's Guide
1107 | Copyright © 2018 Cisco Systems, Inc.
1108 | Licensed under the Apache License Version 2.0 (full copyright notice.).
1109 | Revised January 2019 for Chez Scheme Version 9.5.1
1110 | about this book
1111 |
--------------------------------------------------------------------------------
/10.LibrariesAndTop-levelPrograms.md:
--------------------------------------------------------------------------------
1 | # 库和顶层程序
2 |
3 | 《Scheme语言修订6报告》描述了两类可移植代码单元:库和顶层程序。库是一种关于绑定的具名集合,具有一组显式导出的绑定的声明,一组导入库的声明,以及一个用于初始化它的绑定的程序体。顶层程序程序是一种独立程序,具有一组已声明的导入库和一个在运行顶层程序时才会运行的主体。 只有在顶层程序直接或间接使用库时,才会创建库中的绑定并运行其初始化代码。
4 |
5 | 库和顶层程序中出现的 `import` 声明有两个用途:第一,加载被导入的库;第二,使得被导入库中的绑定在导入到的库或顶层程序中可见。库通常存储在文件系统中,每个文件有一个库,库名通常标识库的文件系统路径,可能相对于默认或程序员指定的库位置集。运行顶层程序和加载库的确切机制取决于实现。
6 |
7 | 本章介绍了在 Chez Scheme 中加载库和程序的机制,以及用于控制和跟踪此过程的各种功能。它还描述了一组内置库和语法形式,用于定义库或顶层程序文件之外的新库和顶层程序。
8 |
9 | ## 10.1. 内置库
10 |
11 | 除了经《Scheme语言修订6报告》规定的 RNRS 库:
12 |
13 | ```scheme
14 | (rnrs base (6))
15 | (rnrs arithmetic bitwise (6))
16 | (rnrs arithmetic fixnums (6))
17 | (rnrs arithmetic flonums (6))
18 | (rnrs bytevectors (6))
19 | (rnrs conditions (6))
20 | (rnrs control (6))
21 | (rnrs enums (6))
22 | (rnrs eval (6))
23 | (rnrs exceptions (6))
24 | (rnrs files (6))
25 | (rnrs hashtables (6))
26 | (rnrs io ports (6))
27 | (rnrs io simple (6))
28 | (rnrs lists (6))
29 | (rnrs mutable-pairs (6))
30 | (rnrs mutable-strings (6))
31 | (rnrs programs (6))
32 | (rnrs r5rs (6))
33 | (rnrs records procedural (6))
34 | (rnrs records syntactic (6))
35 | (rnrs records inspection (6))
36 | (rnrs sorting (6))
37 | (rnrs syntax-case (6))
38 | (rnrs unicode (6))
39 | ```
40 |
41 | Chez Scheme 还提供了两个额外的库:`(chezscheme)` 和 `(chezscheme csv7)`。前者也可以引用为 `(scheme)`,后者也可以引用为 `(scheme csv7)` 。
42 |
43 | 本文档中描述了 `chezscheme` 库所导出的每个标识符绑定,包括像 `lambda`、辅助关键字 `else`、模块名 `scheme` 和过程名 `cons`。在大多数情况下,从 `(chezscheme)` 库导出的标识符对应于从 RNRS 其中一个库导出的标识符,绑定是相同的。然而,在某些情况下,`(chezscheme)` 绑定以某种方式扩展了 `rnrs` 绑定。例如,`(chezscheme) syntax-rules` 形式允许其子句具有防护板(第11.2节),而 `(rnrs) syntax-rules` 形式则不允许。类似地,`(chezscheme) current-input-port` 过程接受一个可选的端口参数,指定该参数后,会将当前输入端口设置为 `port`(第9.8节),而 `(rnrs) current-input-port` 过程则不接受该参数。当 `(chezscheme)` 库以某种方式扩展 RNRS 绑定时,`(chezscheme)` 库还导出 RNRS 版本,其名称前缀为 `r6rs:`,例如 `r6rs:syntax-rules` 或 `r6rs:current-input-port`。
44 |
45 | 第7版 `(chezscheme csv7)` 向后兼容库包含一组语法形式和过程的绑定,这些语法或过程语义与 RNRS 中相同标志服的绑定直接冲突。以下标识符从 `(chezscheme csv7)` 导出。
46 |
47 | ```scheme
48 | record-field-accessible?
49 | record-field-accessor
50 | record-field-mutable?
51 | record-field-mutator
52 | record-type-descriptor
53 | record-type-field-decls
54 | record-type-field-names
55 | record-type-name
56 | record-type-symbol
57 | ```
58 |
59 | 这个库的绑定应仅用于旧代码;新代码应使用 RNRS 变体。这些标识符的每一个都可以在 `(chezscheme)` 库中带上前缀 `csv7:` 来使用,例如 ` csv7:record-type-name` 。
60 |
61 | 作用于库或 RNRS 顶层程序范围之外的代码的交互环境包含 `(chezscheme)` 库的所有绑定,如第2.3节所述。
62 |
63 | ## 10.2. 运行顶层程序
64 |
65 | 顶层程序必须存在于其自己的文件中,该文件可以具有任何名字,并且可以保存在文件系统中的任何位置。存在于文件中的顶层程序由以下三种机制之一运行:`scheme-script` 命令,`--program` 命令行参数或 `load-program` 程序。
66 |
67 | `scheme-script` 的使用方式如下:
68 |
69 | ```shell
70 | scheme-script program-filename arg ...
71 | ```
72 |
73 | 它也可以在基于 Unix 的系统上隐式运行,方法是将下面这行代码
74 |
75 | ```shell
76 | #! /usr/bin/env scheme-scrip
77 | ```
78 |
79 | 放在包含顶层程序的文件的最顶端,并且使顶层程序文件可执行,然后执行该文件。这一行也可以替换为:
80 |
81 | ```shell
82 | #! /usr/bin/scheme-script
83 | ```
84 |
85 | 可以将 `/usr/bin` 替换为包含 `scheme-script` 的目录的绝对路径(如果它不在`/usr/bin`中)。 第一种形式在《Scheme语言修订6报告》[29]的非规范性附录中被推荐使用,并且在路径中出现 `scheme-script` 的任何地方都可以使用。
86 |
87 | 对 `scheme` 或 `petite` 可执行文件来说,`--program` 命令的使用方式类似,通过运行:
88 |
89 | ```shell
90 | scheme --program program-filename arg ...
91 | petite --program program-filename arg ...
92 | ```
93 |
94 | 或者通过在顶层程序文件的最顶端包含以下代码:
95 |
96 | ```shell
97 | #! /usr/bin/scheme --scrip
98 | ```
99 |
100 | 或
101 |
102 | ```shell
103 | #! /usr/bin/petite --script
104 | ```
105 |
106 | 并使文件可执行来执行程序。再次强调,如果 `scheme` 或者 `petite` 不存在于 `/usr/bin`,那么 `/usr/bin` 就应该被替换为包含它们的实际目录的绝对路径。
107 |
108 | 在 12.4 节描述的 `load-program` 过程,其使用方式如同 `load` :
109 |
110 | ```scheme
111 | (load-program string)
112 | ```
113 |
114 | 其中 `string` 代表了存储顶层程序的文件名。
115 |
116 | 无论使用何种机制,如果开头行是上述形式之一,或者更一般地说,由 `#!` 后跟空格或正斜杠组成,则开头行不被视为程序的一部分部分,一旦 Scheme 系统启动并开始运行程序,它将被忽略。因此,即使在由 `load-program` 加载的文件中,该行也可能存在。事实上,上述其他两种机制正是通过第12.8节中描述的 `scheme-program` 参数的值,最终调用 `load-program` ,并由 `load-program` 在程序求值之前扫描 `#!` 行(如果存在)。
117 |
118 | 可以使用第 12.4 节中描述的 `compile-program` 过程编译顶层程序。` compile-program` 将 `#!` 行从源文件复制到目标文件,后面接着源代码的编译版本。除了内置库之外,顶层程序所依赖的任何库都必须首先通过 `compile-file` 或 `compile-library` 进行编译。 这可以手动完成,也可以在编译程序之前将参数对象 `compile-imported-libraries` 设置为 `#t` 来实现。如果它所依赖的任何库被重新编译,则该程序也需要重新编译。通过上述各种机制,编译后的顶层程序可以像源文件顶层程序一样运行。
119 |
120 | 在 Chez Scheme 中,库也可以在 REPL 中定义,或者放在要通过 `load` 或 `load-library` 加载的文件中。无论库是放在自己的文件中还是通过 `import` 隐式地加载、键入到 REPL 中或者放在待通过 `load` 求值的、顶层表达式的单独文件中,其定义的语法都是相同的。顶层程序也可以在 REPL 中定义,或者放在要通过 `load` 加载的文件中,但在这种情况下,语法略有不同。在《Scheme语言修订6报告》定义的语言中,顶层程序仅仅是一个展开的形式序列,由 `import` 形式和主体组成,仅由其所在文件的边界分隔。为了在 REPL 中键入顶层程序或将其置于要通过 `load` 加载的文件中,Chez Scheme 允许将顶层程序包含在 `top-level-program` 形式中。
121 |
122 | ## 10.3 库和顶层程序形式
123 |
124 | + 语法:`(library name exports imports library-body)`
125 | + 返回:未定义
126 | + 所属库:`(chezscheme)`
127 |
128 | `library` 形式定义了一个名称为 `name` 、导出表为 `exports`、导入表为 `imports` 以及程序体为 `body` 的新库。有关库形式语法和语义的详细信息,请参见《Scheme 程序设计语言(第4版)》的第10.3节以及《Scheme语言修订6报告》。
129 |
130 | 在任何给定时间里,只能加载库的一个版本,如果在已加载库的另一个版本后、通过 `import` 隐式地加载库,则会引发异常。Chez Scheme 允许将库的不同版本或相同版本的新实例显式键入到 REPL 中或从文件中显式加载,以便于交互式测试和调试。程序员应该注意确保使用该库的任何代码也被重新输入或重新加载,以确保代码访问的是库新实例的绑定。
131 |
132 | ```scheme
133 | (library (test (1)) (export x) (import (rnrs)) (define x 3))
134 | (import (test))
135 | (define f (lambda () x))
136 | (f) ; ==> 3
137 |
138 | (library (test (1)) (export x) (import (rnrs)) (define x 4))
139 | (import (test))
140 | (f) ; ==> 3 ; oops---forgot to redefine f (define f (lambda () x))
141 | (f) ; ==> 4
142 |
143 | (library (test (2)) (export x) (import (rnrs)) (define x 5))
144 | (import (test))
145 | (define f (lambda () x))
146 | (f) ; ==> 5
147 | ```
148 |
149 | 与模块导入一样(第11.5节),库 `import` 也可能出现在定义可能出现的任何地方,包括在 REPL 的顶层、由 `load` 加载的文件,或者 `lambda`、`let`、`letrec`,`letrec*` 等的主体中。可以使用相同的 `import` 形式来导入库和模块。
150 |
151 | ```scheme
152 | (library (foo) (export a) (import (rnrs)) (define a 'a-from-foo))
153 | (module bar (b) (define b 'b-from-bar))
154 | (let () (import (foo) bar) (list a b)) ; => (a-from-foo b-from-bar)
155 | ```
156 |
157 | 除非库从 `(chezscheme)` 库中导入 `import` 关键字,否则它在库的主体中不可见。
158 |
159 | + 语法:`(top-level-program imports body)`
160 | + 返回:未定义
161 | + 所属库:`(chezscheme)`
162 |
163 | `top-level-program` 形式可以键入到 REPL 中,也可以放在要通过 `load` 加载的文件中,其行为就好像它的子形式放在文件中并通过 `load-program` 加载一样。顶层程序语法和语义在《Scheme 程序设计语言(第4版)》的第10.3节以及《Scheme语言修订6报告》中有具体的描述。
164 |
165 | 下面的代码演示了在 REPL 中如何使用 `top-level-program`:
166 |
167 | ```scheme
168 | > (top-level-program (import (rnrs))
169 | (display "hello!\n"))
170 | hello!
171 | ```
172 |
173 | ## 10.4 独立的 `import` 和 `export` 形式
174 |
175 | 尽管《Scheme语言修订6报告》没有要求,但 Chez Scheme 支持使用独立的 `import` 和 `export` 形式。`import` 形式可以出现在其他定义可以出现的任何地方,包括 `library` 的主体、`module`(第 11.5 节)的主体、`lambda` 或其他局部主体以及程序顶层。`export` 形式可以出现在 `library` 或 `module` 主体的定义中,以指定 `library` 或 `module` 的其他导出。
176 |
177 | 在库或顶层程序中,这些形式的关键字必须从 `(chezscheme)` 库中导入才能使用,因为它们未在任何《Scheme语言修订6报告》中定义。
178 |
179 | + 语法:`(import import-spec ...)`
180 | + 语法:`(import-only import-spec ...)`
181 | + 返回:未定义
182 | + 所属库:`(chezscheme)`
183 |
184 | `import` 或 `import-only` 形式是一条定义,可以出现在其他定义可以出现的任何地方,包括在程序的顶层、嵌套在 `lambda` 表达式的主体中以及嵌套在模块和库中。
185 |
186 | 每条 `import-spec` 必须采用以下形式之一:
187 |
188 | ```scheme
189 | import-set
190 | (for import-set import-level ...)
191 | ```
192 |
193 | 《Scheme 程序设计语言(第4版)》的第10章描述了 `for` 包装语法和 `import-level` 。Chez Scheme 会忽略它们,它们会根据《Scheme语言修订6报告》允许自动确定必须导入的标识符的级别。这使程序员免于这样做的义务,并使编译和运行期实际导入的库集更通用和更精确 [21,19]。
194 |
195 | `import-sert` 必须采用以下形式之一:
196 |
197 | ```scheme
198 | library-spec
199 | module-name
200 | (only import-set identifier ...)
201 | (except import-set identifier ...)
202 | (prefix import-set prefix)
203 | (add-prefix import-set prefix)
204 | (drop-prefix import-set prefix)
205 | (rename import-set (import-name internal-name) ...)
206 | (alias import-set (import-name internal-name) ...)
207 | ```
208 |
209 | 这些形式中有几个由《Scheme语言修订6报告》指定;其余的是 Chez Scheme 扩展,包括 `module-name`、`add-prefix`、`drop-prefix` 以及 `alias` 形式。
210 |
211 | `import` 或 `import-only` 形式使指定的绑定在它们出现的范围内可见。除了在顶层,两者的不同之处在于:除了被所导入的名字遮蔽起来的绑定,`import` 将所有绑定可见,而 `import-only` 则会隐藏所有已有绑定,即,仅使导入的名字可见。在顶层,`import-only` 和 `import` 具有相同的行为。
212 |
213 | 每一条 `import-set` 按下面的方式指明了要如何使名字可见:
214 |
215 |
216 | library-spec
217 | - 所有被《Scheme语言修订6报告》指明的库的导出
218 |
219 | module-name
220 | - 由标识符
module-name
指定的模块的所有导出
221 |
222 | (only import-set identifier ...)
223 | - 对于由
import-set
指定的导出,仅导出 identifier ...
224 |
225 | (except import-set identifier ...)
226 | - 除
identifier ...
以外的由 import-set
指定的所有标识符
227 |
228 | (prefix import-set prefix)
229 | - 所有由
import-set
指定的标识符并添加前缀 prefix
230 |
231 | (add-prefix import-set prefix)
232 | - 所有由
import-set
指定的标识符并添加前缀 prefix
(类似于 prefix
)
233 |
234 | (drop-prefix import-set prefix)
235 | - 所有由
import-set
指定的标识符并移除前缀 prefix
236 |
237 | (rename import-set (import-name internal-name) ...)
238 | - 所有由
import-set
指定的标识符并会根据每对规则将 import-name
重命名为对应的标识符 internal-name
239 |
240 | (alias import-set (import-name internal-name) ...)
241 | - 所有由
import-set
指定的标识符并根据每对规则为 import-name
添加别名 internal-name
242 |
243 |
244 |
245 | 与 `rename` 形式的不同,`alias` 形式中的 `import-name` 和 `internal-name` 都会存在于结果集合中,而不只是保留 `internal-name` 。
246 |
247 | 如果因为导出或前缀的缺失,导致给定的选取或变换无法完成,则会产生**语法违规(Syntax Violation)**。
248 |
249 | 对通过导入模块或库而变得可见的标识符来说,它的作用域就好像它的定义出现在导入发生的地方一样。以下示例使用局部模块 `m` 说明了这些作用域规则。
250 |
251 | ```scheme
252 | (library (A) (export x) (import (rnrs)) (define x 0))
253 | (let ([x 1])
254 | (module m (x setter)
255 | (define-syntax x (identifier-syntax z))
256 | (define setter (lambda (x) (set! z x)))
257 | (define z 2))
258 | (let ([y x] [z 3])
259 | (import m (prefix (A) a:))
260 | (setter 4)
261 | (list x a:x y z))) ;==> (4 0 1 3)
262 | ```
263 |
264 | 内部 `let` 表达式将 `y` 绑定到外部 `let` 绑定的 `x` 的值。 `m` 的导入使得 `x` 和 `setter` 的定义在内部 `let` 中可见。`(A)` 的导入使得从 `(A)` 导出的变量 `x` 在内部 `let` 的主体内以 `a:x` 的形式可见。因此,在表达式 `(list x a:x y z)` 中,`x` 指的是从 `m` 导出的标识符宏,而 `a:x` 指的是从 `(A)` 导出的变量 `x`,而 `y` 和 `z` 指的是由内部 `let` 建立的绑定。标识符宏 `x` 扩展为对模块中定义的变量 `z` 的引用。
265 |
266 | 对于局部导入形式,很少需要使用扩展导入说明符。例如,封装了导入和引用的抽象可以很容易地定义,并按如下方式使用:
267 |
268 | ```scheme
269 | (define-syntax from
270 | (syntax-rules ()
271 | [(_ m id) (let () (import-only m) id)]))
272 |
273 | (library (A) (export x) (import (rnrs)) (define x 1))
274 | (let ([x 10])
275 | (module M (x) (define x 2))
276 | (cons (from (A) x) (from M x))) ; ==> (1 . 2)
277 | ```
278 |
279 | 可以不用 `import-only` 而用 `import` 来定义 `from` ,但是如果没有从库或模块中成功导出指定的标识符,使用 `import-only` 就会得到反馈。如果使用 `import` 而非 `import-only`,那么当库或模块并没有导出指定的名字时,当前的绑定可能会变得可见。
280 |
281 | ```scheme
282 | (define-syntax lax-from
283 | (syntax-rules ()
284 | [(_ m id) (let () (import m) id)]))
285 |
286 | (library (A) (export x) (import (rnrs)) (define x 1))
287 |
288 | (let ([x 10])
289 | (module M (x) (define x 2))
290 | (+ (from (A) x) (from M y))) ; ==> exception: unbound identifier y
291 |
292 | (let ([x 10] [y 20])
293 | (module M (x) (define x 2))
294 | (+ (lax-from (A) x) (lax-from M y))) ; ==> 21
295 | ```
296 |
297 | 正如人们所期望的那样,导入的可见性与卫生宏展开是以这样一种方式相互作用的:从模块 `M` 导入的标识符 `x` 在导入上下文中被处理,就好像相应的导出标识符与 `M` 一起存在于导入形式中。
298 |
299 | 上面的 `from` 抽象是有效的,因为 `M` 和 `id` 都出现在抽象的输入中,所以导入的 `id` 捕获了对 `id` 的引用。
300 |
301 | `from` 形式的以下变体也有效,因为所有的名字都是由转换器引入输出的。
302 |
303 | ```scheme
304 | (module M (x) (define x 'x-of-M))
305 | (define-syntax x-from-M
306 | (syntax-rules ()
307 | [(_) (let () (import M) x)]))
308 | ```
309 |
310 | 另一方面,由 `import` 引入的模块名字不会捕获自由引用。
311 |
312 | ```scheme
313 | (let ([x 'local-x])
314 | (define-syntax alpha
315 | (syntax-rules ()
316 | [(_ var) (let () (import M) (list x var))]))
317 |
318 | (alpha x)) ; ==> (x-of-M local-x)
319 | ```
320 |
321 | 类似地,来自于模块中自由变量的导入,不会捕获对引入变量的引用。
322 |
323 | ```scheme
324 | (let ([x 'local-x])
325 | (define-syntax beta
326 | (syntax-rules ()
327 | [(_ m var) (let () (import m) (list x var))]))
328 |
329 | (beta M x)) ; ==> (local-x x-of-M)
330 | ```
331 |
332 | 通过扩展的 `import` 描述符 `prefix`、`rename` 以及 `alias` 引入的**前缀化(prefixed)**、**易名的(renamed)**以及**别名的(aliased)**绑定同样符合这样的语义。
333 |
334 | `from` 抽象适用于变量,但不适用于导出的关键字、记录名或模块名,因为输出是一个表达式,因此只能出现在表达式可出现的地方。在 `import*` 的以下定义中,泛化使用了此技术,它支持重命名导入绑定和选择性导入特定绑定——无需使用内置的 `import` 子形式来选取和重命名标识符。
335 |
336 | ```scheme
337 | (define-syntax import*
338 | (syntax-rules ()
339 | [(_ m) (begin)]
340 | [(_ m (new old))
341 | (module (new)
342 | (module (tmp)
343 | (import m)
344 | (alias tmp old))
345 | (alias new tmp))]
346 | [(_ m id) (module (id) (import m))]
347 | [(_ m spec0 spec1 ...)
348 | (begin (import* m spec0) (import* m spec1 ...))]))
349 | ```
350 |
351 | 为了选择性地从模块或库 `m` 中导入标识符,`import*` 形式扩展为一个匿名模块,该模块首先导入 `m` 的所有导出,然后仅重新导出选定的标识符。为了在导入时重命名,宏会扩展为匿名模块,该模块会导出绑定到名字的别名(第 11.10 节)。
352 |
353 | 如果输出将 `new` 的定义放在与 `m` 的导入相同的作用域内,那么只要 `new` 也出现在 `m` 的接口中,就会出现命名冲突。为了防止这种情况,输出将导入放置在嵌套的匿名模块中,并通过引入的标识符 `tmp` 的别名链接新旧模块。
354 |
355 | 宏通过递归地展开来处理多个导入规范。以下每个示例都将 `cons` 导入为 `+` 并将 `+` 导入为 `cons`,这可能不是一个好主意。
356 |
357 | ```scheme
358 | (let ()
359 | (import* scheme (+ cons) (cons +))
360 | (+ (cons 1 2) (cons 3 4))) ; ==> (3 . 7)
361 |
362 | (let ()
363 | (import* (rnrs) (+ cons) (cons +))
364 | (+ (cons 1 2) (cons 3 4))) ; ==> (3 . 7)
365 | ```
366 |
367 | + 语法:`(export export-spec ...)`
368 | + 返回:未定义
369 | + 所属库:`(chezscheme)`
370 |
371 | `export` 形式是一条定义,可以与其他定义一起出现在 `library` 或 `module` 的前面。`export` 形式出现在其他上下文中是一种语法错误,包括顶层、顶层程序或 `lambda` 主体的定义中。
372 |
373 | 每条 `export-spec` 必须采用以下形式之一:
374 |
375 | ```scheme
376 | identifier
377 | (rename (internal-name export-name) ...)
378 | (import import-spec ...)
379 | ```
380 |
381 | 其中每个 `internal-name` 和 `export-name` 都是一个标识符。前两种在语法上与 `library export-spec` 相同,而第三中在语法上与 Chez Scheme `import` 形式相同,后者是 R6RS 库 `import` 自形式的扩展。第一种形式以 `identifier` 命名单个导出,其导出名字与其内部名字相同。第二个命名一组导出,每个导出名字都明确给出并且可能与其内部名字不同。
382 |
383 | 对于第三种,由 `import` 形式指定的标识符都成为了导出,可以通过 `import-spec` 来指定其别名、重命名、前缀等。对于那些其中的绑定由一条出现在 `export` 形式中的 `import` 形式导出的库或模块,可以在导出模块和库的内部或外部定义,并且不需要在导出模块或库中的其他地方导入。
384 |
385 | 下面的库导出了 `if` 的一种 `two-armed-only` 变体,以及 `(rnrs)` 库的其余所有绑定。
386 |
387 | ```scheme
388 | (library (rnrs-no-one-armed-if) (export) (import (except (chezscheme) if))
389 | (export if (import (except (rnrs) if)))
390 | (define-syntax if
391 | (let ()
392 | (import (only (rnrs) if))
393 | (syntax-rules ()
394 | [(_ tst thn els) (if tst thn els)]))))
395 |
396 | (import (rnrs-no-one-armed-if))
397 | (if #t 3 4) ; ==> 3
398 | (if #t 3) ; ==> exception: invalid syntax
399 | ```
400 |
401 | 定义相同库的另一种方式则是使用一个不同的内部名字来定义 `two-armed-only-if`,并在导出时使用 `rename` 将其重命名为 `if` :
402 |
403 | ```scheme
404 |
405 | (library (rnrs-no-one-armed-if) (export) (import (chezscheme))
406 | (export (rename (two-armed-if if)) (import (except (rnrs) if)))
407 | (define-syntax two-armed-if
408 | (syntax-rules ()
409 | [(_ tst thn els) (if tst thn els)])))
410 |
411 | (import (rnrs-no-one-armed-if))
412 | (if #t 3 4) ; ==> 3
413 | (if #t 3) ; ==> exception: invalid syntax
414 | ```
415 |
416 | `export` 形式在库语句体中的位置无关紧要,例如,`export` 形式可以出现在上述例子中定义之后的任何地方。
417 |
418 | + 语法:`(indirect-export id indirect-id ...)`
419 | + 返回:未定义
420 | + 所属库:`(chezscheme)`
421 |
422 | 本形式是一条定义,可以出现在其他定义可出现的任何地方。
423 |
424 | `indirect-export` 形式表明当 `id` 被导出到顶层时,由 `indirect-id` 指明的那些标识符也被间接地导出到顶层。
425 |
426 | 通常来说,如果标识符没有被库或模块直接导出,那么只可能由展开在该库或模块中定义且被导出的宏时,从外部引用该标识符。尽管如此,这也不可能发生在定义在顶层的库或模块(或嵌套在其他库或模块)中,除非 (1) 库或模块已设置为将所有标识符隐式导出为间接导出,或 (2) 每个被间接导出的标识符都被显式声明为某个其他标识符的间接导出,这些标识符通过 `indirect-export` 或 `module` 导出自形式中内置的间接导出功能从库或模块中直接或间接导出。默认情况下,(1) 对库为真,对模块为假,但可以通过 `implicit-exports` 形式覆盖默认值,如下所述。
427 |
428 | 这种形式仅在顶层库、顶层模块或包含在库或顶层模块中的模块中才有意义,尽管如此,如果库或模块已经隐式导出所有绑定,则它没有效果。 然而,它被允许出现在任何其他定义可以出现的地方,因此可以在任何定义上下文中使用展开为间接导出形式的宏。
429 |
430 | 间接导出的标识符罗列起来,使得编译器可以确定必须插入顶层环境的确切绑定集合(直接和间接),相反,可以更有效地将绑定集视为局部绑定(并且如果它们没有被使用,可能被丢弃)。
431 |
432 | 在下面的示例中,当 `current-count` 导出到顶层时,`indirect-export` 用于将 `count` 间接导出到顶层。
433 |
434 | ```scheme
435 | (module M (bump-count current-count)
436 | (define-syntax current-count (identifier-syntax count))
437 | (indirect-export current-count count)
438 | (define count 0)
439 | (define bump-count
440 | (lambda ()
441 | (set! count (+ count 1)))))
442 |
443 | (import M)
444 | (bump-count)
445 | current-count ; ==> 1
446 | count ; ==> exception: unbound identifier count
447 | ```
448 |
449 | `indirect-export` 形式不要求 `count` 对 `bump-count` 可见,因为它是一个过程,其代码包含在模块中,它不是一个宏,它可能在模块外部的某处展开为对 `count` 的引用。
450 |
451 | 如果 `a` 展开为对一个可能不是被直接导出的标识符的引用,那么在宏的输出中使用 `indirect-export` 就会非常有用,下面演示了上述模块 `M` 的替代定义:
452 |
453 | ```scheme
454 | (define-syntax define-counter
455 | (syntax-rules ()
456 | [(_ getter bumper init incr)
457 | (begin
458 | (define count init)
459 | (define-syntax getter (identifier-syntax count))
460 | (indirect-export getter count)
461 | (define bumper
462 | (lambda ()
463 | (set! count (incr count)))))]))
464 |
465 | (module M (bump-count current-count)
466 | (define-counter current-count bump-count 0 add1))
467 | ```
468 |
469 | + 语法:`(implicit-exports #t)`
470 | + 语法:`(implicit-exports #f)`
471 | + 返回:未定义
472 | + 所属库:`(chezscheme)`
473 |
474 | `implicit-exports` 形式是一个定义,可以与其他定义一起在 `library` 或 `module` 之前出现。`implicit-exports` 形式出现在其他上下文中则是语法错误,包括在顶层或顶层程序的定义或 `lambda` 表达式的主体中。
475 |
476 | `implicit-exports` 形式决定,如果库或模块中的任意元绑定(关键字、元定义或属性定义)被直接导出到最高层,是否要自动间接地将一个不是从该模块或库直接导出的标识符导出到最高层。该形式的默认是值,对库来说是 `#t` ,而对于模块来说是 `#f`,以匹配《Scheme语言修订6报告》的要求。`implicit-exports` 形式仅在库、顶层模块或包含在库或顶层模块中的模块中有意义。它允许出现在 `lambda`、`let` 或类似主体中包含的模块中,但在那里被忽略,因为该模块的绑定都不能导出到顶层。
477 |
478 | `(implicit-exports #t)` 的优点是间接导出不需要显式列出,这很方便。一个缺点是它通常会导致更多的绑定被提升到顶层,在这种情况下它们不能被优化器视为无用而丢弃。对于模块,另一个缺点是不能证明这种绑定是不可变的,这会抑制如过程内联这样的重要的优化。这个缺点导致运行时性能显着降低。
479 |
480 | ## 10.5. 显式调用库
481 |
482 | + 过程:`(invoke-library libref)`
483 | + 返回:未定义
484 | + 所属库:`(chezscheme)`
485 |
486 | `libref` 必须是库引用形式的 S-表达式。库引用的语法在 《Scheme 程序设计语言(第4版) 的第 10 章和《Scheme语言修订6报告》中给出。
487 |
488 | 库会在库外某条表达式(比如在另一个库或顶层程序中)对库导出变量之一的引用求值时或求值之前被隐式地调用。当库被调用时,它的表达式主体(库中变量定义及其初始化表达式的右侧)会被调用。一旦被调用,除非它是首次显式地重新定义或重新加载,否则该库就不会在同一个进程中再次被调用。
489 |
490 | `invoke-library` 显式调用 `libref` 指定的库,如果它尚未被调用或调用后又被重新定义或重新加载。如果库尚未加载,则 `invoke-library` 首先通过第 2.4 节中描述的过程加载库。
491 |
492 | `invoke-library` 通常只对那些具有副作用的库有用。这在控制副作用何时发生并强制调用没有导出变量的库时很有用。调用库不会强制加载或求值编译期代码(宏转换器表达式和元定义),也不会导致库的绑定变得可见。
493 |
494 | 避免在库主体中出现外部可见的副作用是一种很好的做法,这样可以很好地使库在编译期和运行期被同等使用。 如果可行,请考虑将库主体的副作用移至初始化例程,并添加一个顶层程序用于导入库并调用初始化例程。使用这种结构,对库的 `invoke-library` 的调用可以被对顶层程序的 `load-program` 的调用所替代。
495 |
496 | ## 10.6 库参数
497 |
498 | 下面的参数描述了,当试图去加载一个库时,应该从何处查找导入、是否编译所加载的库、进行搜寻时是否显示跟踪消息。
499 |
500 | + 线程级参数:`library-directories`
501 | + 线程级参数:`library-extensions`
502 | + 所属库:`(chezscheme)`
503 |
504 | 参数 `library-directories` 确定包含库源代码和目标代码的文件在文件系统中的位置,参数 `library-extensions` 确定包含代码的文件的文件扩展名,如第 2.4 节所述。两个参数的值都是字符串序对组成的表。每对 `library-directories` 中的第一个字符串标识源文件根目录,第二个字符串标识相应的目标文件根目录。类似地,每个 `library-extensions` 对中的第一个字符串标识源文件扩展名,第二个字符串标识相应的目标文件扩展名。库源文件或目标文件的完整路径由源文件或目标文件根后跟以斜杠为前缀的库名称组成部分组成,并在末尾添加了库扩展名。例如,对于根目录 `/usr/lib/scheme`、库名称 `(app lib1)` 和扩展名 `.sls`,完整路径为 `/usr/lib/scheme/app/lib1.sls` 。如果库名部分形成绝对路径名,例如 `~/.myappinit` ,则忽略 `library-directories` 参数并且不添加前缀。
505 |
506 | 这些参数的初始值如下所示:
507 |
508 | ```scheme
509 | (library-directories) ; ==> (("." . "."))
510 |
511 | (library-extensions) ; ==> ((".chezscheme.sls" . ".chezscheme.so")
512 | ; (".ss" . ".so")
513 | ; (".sls" . ".so")
514 | ; (".scm" . ".so")
515 | ; (".sch" . ".so"))
516 | ```
517 |
518 | 为方便起见,当设置了这些参数中的任何一个时,表中的任何元素都可以指定为单个源字符串,在这种情况下,将自动确定对象字符串。对于 `library-directories` ,目标字符串与源字符串相同,有效地将同一目录命名为源代码和目标代码根目录。 对于 `library-extension`,对象字符串是从字符串中删除最后一个(或唯一)扩展并附加 `".so"` 的结果。 `library-directories` 和 `library-extensions` 参数也接受第 2.5 节中描述的格式的输入字符串作为 `--libdirs` 和 `--libexts` 命令行选项。
519 |
520 | + 线程级参数:`compile-imported-library`
521 | + 所属库:`(chezscheme)`
522 |
523 | 当此参数的值为 `#t` 时,对于那些目标文件缺失、目标文件编译时间早于源文件或通过 `include` 包含进来的任意源文件、或者本身需要一个库被重新编译(如2.4节所述)的库,`import` 在导入它们时会自动调用 `compile-library-handler` 参数对应的值(默认是一个简单地调用 `compile-library` 的过程)该参数的默认初始值为 `#f`。 它可以通过命令行选项 `--compile-imported-libraries` 设置为 `#t`。
524 |
525 | 当 `import` 通过此机制编译库时,它不会同时加载已编译的库,因为这会导致库的某些部分被重新求值。正因如此,该文件中 `library` 形式之外的运行期表达式不会被求值。如果存在此类表达式并需要对其求值,则应显式加载该库。
526 |
527 | + 线程级参数:`import-notify`
528 | + 所属库:`(chezscheme)`
529 |
530 | 当新参数 `import-notify` 设置为真值时,`import` 会在搜索包含需要加载的每个库的文件时向控制台输出端口显示消息。该参数的默认值为 `#f`。
531 |
532 | + 线程级参数:`library-search-handler`
533 | + 所属库:`(chezscheme)`
534 |
535 | 参数的值必须是一个过程,并且该过程遵循下面为`default-library-search-handler` 描述的协议,这也是该参数的默认值。
536 |
537 | 在导入、编译整个程序或编译整个库期间调用此参数的值来定位库的源代码或目标代码。
538 |
539 | `import`、`compile-whole-program` 以及 `compile-whole-library` 过程在处理程序库期间,会调用该参数的值来定位源文件或目标代码。
540 |
541 | + 过程:`(default-library-search-handler who library directories extensions)`
542 | + 返回值:见下
543 | + 所属库:`(chezscheme)`
544 |
545 | 此过程是 `library-search-handler` 的默认值,会在 `import`、`compile-whole-program` 或 `compile-whole-library` 期间调用以定位库的源代码或目标代码。 `who` 是在 `import-notify` 消息中提供上下文的符号。 `library` 是所需库的名称。`directories` 是一张由 `library-directories` 返回形式的组织起来的源目录和对象目录序对组成的表。`extensions` 是以 `library-extensions` 返回的形式的源和对象扩展名序对组成的表。
546 |
547 | 此过程搜索指定的目录,直到找到具有指定扩展名之一的库源文件或目标文件。如果首先找到源文件,则构造相应的目标文件路径并检查该文件是否存在。如果它首先找到目标文件,则该过程在与该目标目录配对的源目录中查找具有给定源扩展名之一的相应源文件。该过程返回三个值:库源文件的文件系统路径或 `#f`(如果未找到)、相应目标文件的文件系统路径(可能是 `#f`)以及一个布尔值用来表示该目标文件是否存在。
548 |
549 | ## 10.7 审查库
550 |
551 | + 过程:`(library-list)`
552 | + 返回:一组由当前定义的库所组成的表
553 | + 所属库:`(chezscheme)`
554 |
555 | 最初定义的库集包括上面第 10.1 节中列出的库。
556 |
557 | + 过程:`(library-version libref)`
558 | + 返回:指定库的版本
559 | + 过程:`(library-exports libref)`
560 | + 返回:由指定库中的导出组成的表
561 | + 过程:`(library-requirements libref)`
562 | + 返回:由指定库所依赖的库组成的表
563 | + 过程:`(library-requirements libref options)`
564 | + 返回:由指定库所依赖的库组成的表,依据 `options` 过滤
565 | + 过程:`(library-object-filename libref)`
566 | + 返回:返回指定库对应目标文件的文件名,如果有的话
567 | + 所属库:`(chezscheme)`
568 |
569 | 只能获取内置的或先前加载到系统中的库的信息。`libre`f 必须是库引用形式的 S-表达式。库引用的语法在《Scheme 程序设计语言(第四版)》的第 10 章和《Scheme语言修订6报告》中给出。
570 |
571 | `library-version` 返回值是代表库版本的数字表(可能为空)。
572 |
573 | `library-exports` 返回的导出表是一张由符号组成的表,每个符号都对应了库的一个导出。元素出现的顺序是未指定的。
574 |
575 | 当提供可选的 `options` 参数时,它必须有有效的库要求选项的符号上的枚举集,如下面的 `library-requirements-options` 条目中所述。它默认为包含所有选项的集合。`library-requirements` 返回的库列表的每个元素都是库引用的 S 表达式形式。库引用包括系统中存在的库的实际版本(如果非空),即使在导入时未指定版本。库在 library-requirements 返回的列表中出现的顺序未指定。
576 |
577 | 如果指定的库是从目标文件加载或编译到目标文件,则 `library-object-filename` 返回一个字符串表示目标文件的名字。否则,它返回`#f`。
578 |
579 | ```scheme
580 | (with-output-to-file "A.ss"
581 | (lambda ()
582 | (pretty-print
583 | '(library (A (1 2)) (export x z)
584 | (import (rnrs))
585 | (define x 'ex)
586 | (define y 23)
587 | (define-syntax z
588 | (syntax-rules ()
589 | [(_ e) (+ y e)])))))
590 | 'replace)
591 | (with-output-to-file "B.ss"
592 | (lambda ()
593 | (pretty-print
594 | '(library (B) (export x w)
595 | (import (rnrs) (A))
596 | (define w (cons (z 12) x)))))
597 | 'replace)
598 | (compile-imported-libraries #t)
599 | (import (B))
600 | (library-exports '(A)) ; ==> (x z) ; or (z x)
601 | (library-exports '(A (1 2))) ; ==> (x z) ; or (z x)
602 | (library-exports '(B)) ; ==> (x w) ; or (w x)
603 | (library-version '(A)) ; ==> (1 2)
604 | (library-version '(B)) ; ==> ()
605 | (library-requirements '(A)) ; ==> ((rnrs (6)))
606 | (library-requirements '(B)) ; ==> ((rnrs (6)) (A (1 2)))
607 | (library-object-filename '(A)) ; ==> "A.so"
608 | (library-object-filename '(B)) ; ==> "B.so"
609 | ```
610 |
611 | + 语法:`(library-requirements-options symbol ...)`
612 | + 返回:一个 `library-requirements-options` 枚举集
613 | + 所属库:`(chezscheme)`
614 |
615 | `library-requirements-options` 枚举集被传递给 `library-requirements` 以确定要列出的库要求。可用选项如下所述。
616 |
617 |
618 |
619 | import
:
620 | - 包括导入指定库时必须导入的库。
621 |
622 | visit@visit
:
623 | - 包括访问指定库时必须访问的库。
624 |
625 | invoke@visit
:
626 | - 包括访问指定库时必须调用的库。
627 |
628 | invoke
:
629 | - 包括在调用指定库时必须调用的库。
630 |
--------------------------------------------------------------------------------
/12.SystemOperations.md:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/13.StorageManagement.md:
--------------------------------------------------------------------------------
1 | # 第 十三 章 存储系统
2 |
3 | 本章节描述存储管理系统的各方面,以及控制其操作的各种过程。
4 |
5 | ## 第 13.1 节 垃圾回收
6 | Scheme的对象(如序对,字符串,过程)都不会通过scheme程序明确释放。相反,一旦确定对象无法访问,存储管理系统会自动回收与该对象关联的存储空间。为了回收此存储空间,ChezScheme会在程序运行时,定期运行垃圾回收器。
7 | 从已知的入口开始,即机器寄存器,回收器找到所有可访问的对象,复制它们(在大多数情况下),以消除它们之间的碎片,并回收无法访问的对象占用的存储空间。
8 |
9 |
10 | 收集行为是由默认的collect-request处理器触发的,该处理器通过一个collect-request中断完成调用,该中断在分配了大约n个字节的存储空间之后产生,其中n是参数`collect-trip-bytes`的值。默认的collect-request处理器,通过调用名为`collect`的无参数过程,发起一个收集行为。回收处理器可通过改变参数`collect-request-handler`的值来重新定义。一个程序还可以直接通过调用`collect`,来使collect-request中断之间发起一次回收行为。
11 |
12 |
13 | ChezScheme的收集器是基于世代的收集器。它根据对象的世代(初略地说,是收集次数)对其进行隔离,并且收集老代对象的频率比新代对象频率低,由于新代对象往往很容易就无法访问了,所以大多数收集工作只花很少的时间。仅当堆被压缩(查看4.8章节的`Scompact_heap `)或`collect`的`target-generation` 参数被设置为`static`,对象才会被放入静态世代。
14 |
15 |
16 | 非静态世代从0开始递增,直到达到`collect-maximum-generation`的当前值。存储管理系统把最新分配的对象放入第0代。在第0代的回收期间,默认情况下会把留存的第0代对象移动到第一代。同样地,回收第一代期间,会把留存的第0代和第1代对象移至第2代,如此重复。在回收最大非静态代期间,所有留存的非静态对象都将移至最大非静态代。通过这种机制,一个对象有可能跳过一个或多个世代,但这在许多对象上不太可能发生,并且如果这些对象变得不可访问,则最终将回收它们的存储。
17 |
18 |
19 | gc-trip是一个内置的计数器,用来控制何时回收每一代。每次调用不带参数的`collect`,gc-trip自增1。设回收世代的基数为r,已回收的世代是最高代g,则gc-trip是rg的倍数。如果`collect-generation-radix`设置为4,系统每次都回收第0代,每4次回收一次第1代,每16次回收第2代,以此类推。
20 |
21 |
22 | 每次调用单个参数g的`collect`,第g代会回收,并且gc-trip前进到下一个rg的边界,但不超过下一个rg+1边界,其中r还是`collect-generation-radix`的值。
23 |
24 |
25 | 当用第二个参数tg调用`collect`时,tg确定目标代。当g是最大非静态代,tg必须是g,或者是`static`符号。其他情况,tg必须是g或者g+1。当目标代是`static`符号,所有在非静态代的数据都将移至静态代。位于静态代的对象不会被回收。当程序的常驻代码或数据结构加载并初始化后,减少后续的回收开销方面很好用。
26 |
27 |
28 | 通过设置本节中描述的参数,可以对垃圾回收期器的行为进行实质性的调整。通过重新定义collect-request handler来调用带有显式g和tg参数的`collect`,甚至有可能完全覆盖收集器的默认策略,以确定何时回收每个世代。例如,程序员可以通过重新定义handler,显式的g,tg参数调用`collect`,以在长时间内将最大非静态代视为静态生成代,并且在那段时间里绝不会等于最大静态代。
29 |
30 |
31 | 有关ChezScheme收集器的更多信息,请参见报告"Don't stop the BiBOP: Flexible and efficient storage management for dynamically typed languages"。[[13](https://cisco.github.io/ChezScheme/csug9.5/bibliography.html#g162)
32 |
33 |
34 | **过程**: (collect)
35 |
36 | **过程**: (collect g)
37 |
38 | **过程**: (collect g tg)
39 |
40 | **返回**: 未定义
41 |
42 | **库**: (chezscheme)
43 |
44 |
45 | g 必须是一个非负fixnum,不能大于最大非静态代,即`collect-maximum-generation`返回值。
46 |
47 | 如果g是最大非静态代,tg必须是一个等于g的fixnum,或者是符号`static`。其他情况,tg必须是一个等于或大于g的fixnum。
48 |
49 |
50 | 该过程会促使存储管理器发起一次垃圾回收。回收动作是定期调用collect-request处理器,但也可以显式调用它以在特定时间(例如在计时计算之前)强制回收。在ChezScheme的线程版本中,调用collect的线程必须是唯一的活动线程。
51 |
52 | 如本节的引言中所述,系统会根据g和tg(如果提供)确定要回收的世代。
53 |
54 |
55 | **过程**: (collect-rendezvous)
56 |
57 | **返回**: 未定义
58 |
59 | **库**: (chezscheme)
60 |
61 |
62 | 请求一次垃圾回收(与系统指定的进行回收时的方式相同)。所有运行中的线程都会配合,以便其中一个线程顺利调用collect-request处理器。在此期间,其他的线程都暂停直到该处理器返回。
63 |
64 | 请注意,如果collect-request处理器(查看`collect-request-handler`)没有调用`collect`,那么`collect-rendezvous`不会实际地执行一次垃圾回收。
65 |
66 |
67 |
68 | **全局参数**: collect-notify
69 |
70 | **库**: (chezscheme)
71 |
72 |
73 | 如果`collect-notify`设置为#t,每当运行一次回收时,回收器会打印一条信息。`collect-notify`默认值是 #f。
74 |
75 |
76 |
77 | **全局参数**: collect-trip-bytes
78 |
79 | **库**: (chezscheme)
80 |
81 | 此参数确定允许在垃圾回收之间分配的大概存储量。该值必须是正fixnum。
82 |
83 | ChezScheme在内部以大块分配内存,并通过内联操作将这些块细分以提高效率。存储管理器确定是否为每个分配的大块仅请求一次回收。此外,从存储管理器请求回收到兑现回收之间可能需要一段时间,特别是如果通过`with-interrupts-disabled`或`disable-interrupts`暂时禁用了中断。因此,`collect-trip-bytes`仅是一种近似度量。
84 |
85 |
86 |
87 | **全局参数**: collect-generation-radix
88 |
89 | **库**: (chezscheme)
90 |
91 |
92 | 此参数确定默认情况下,collect-request处理器调用不带参数的`collect`时收集每一代的频率。该值必须是正fixnum。
93 | 每回收rg (a . b)
202 |
203 | (define x (cons 'a 'b))
204 | (define p (weak-cons x '()))
205 | (set! x '*)
206 | (collect)
207 | (car p) => #!bwp
208 | ```
209 |
210 | 如果垃圾回收提升序对到第一代先于`(set! x '*)`,以上的后一个示例可能会返回(a . b)。
211 | 可能有必要强制一个老代回收行为来回收该对象。
212 | 存储管理系统仅保证一旦删除了该对象的所有非弱指针,该对象最终将被回收,但不保证何时回收。
213 |
214 |
215 |
216 | **过程**: (weak-pair? obj)
217 |
218 | **返回**: 如果obj是弱对,返回#t,否则返回#f。
219 |
220 | **库**: (chezscheme)
221 |
222 |
223 | ```
224 | (weak-pair? (weak-cons 'a 'b)) => #t
225 | (weak-pair? (cons 'a 'b)) => #f
226 | (weak-pair? "oops") => #f
227 | ```
228 |
229 |
230 |
231 | **过程**: (ephemeron-cons obj1 obj2)
232 |
233 | **返回**: 一个新的星历对
234 |
235 | **库**: (chezscheme)
236 |
237 |
238 | obj1是新对的car,obj2是新对的cdr。星历对与普通对没有区别,除了以下两个方面:
239 |
240 | 1. 星历对可以使用谓词`ephemeron-pair?`以区分普通对。
241 | 2. 星历对的car包含一个指向对象的弱指针,并且只有保留了星历对的car的情况下才保留星历对的cdr。
242 |
243 | 一个星历对的行为类似弱对,但cdr还经过特殊处理:将星历对的cdr设置为#!bwp的同时,会将car设置为#!bwp。那么通过cdr对象去引用car对象时,不一定需要保留car(这跟弱对不同)。相反,出于某种原因,car必须独立于cdr对象保存。
244 |
245 | 像弱对和其他对一样,星历对可以使用`set-car!`和`set-cdr!`对其进行修改。并且,由于没有针对星历对的阅读器语法,星历对打印方式跟普通对一样。
246 |
247 | ```
248 | (define x (cons 'a 'b))
249 | (define p (ephemeron-cons x x))
250 | (car p) => (a . b)
251 | (cdr p) => (a . b)
252 |
253 | (define x (cons 'a 'b))
254 | (define p (ephemeron-cons x x))
255 | (set! x '*)
256 | (collect)
257 | (car p) => #!bwp
258 | (cdr p) => #!bwp
259 |
260 | (define x (cons 'a 'b))
261 | (define p (weak-cons x x)) ; 这不是一个星历对
262 | (set! x '*)
263 | (collect)
264 | (car p) => (a . b)
265 | (cdr p) => (a . b)
266 | ```
267 |
268 | 与弱对一样,如果垃圾回收提升该pair至老代的工作发生在给x赋值*之前,则上面中间示例的最后两个表达式实际上可能会返回(a.b)。
269 | 但是,在上面的最后一个示例中,最后两个表达式的结果将始终为(a.b),因为弱对的cdr拥有非弱引用,并且该非弱引用阻止了car成为#!bwp。
270 |
271 |
272 |
273 |
274 | **过程**: (ephemeron-pair? obj)
275 |
276 | **返回**: 如果obj是星历对,返回#t,否则返回#f。
277 |
278 | **库**: (chezscheme)
279 |
280 | ```
281 | (ephemeron-pair? (ephemeron-cons 'a 'b)) => #t
282 | (ephemeron-pair? (cons 'a 'b)) => #f
283 | (ephemeron-pair? (weak-cons 'a 'b)) => #f
284 | (ephemeron-pair? "oops") => #f
285 | ```
286 |
287 |
288 |
289 | **过程**: (bwp-object? obj)
290 |
291 | **返回**: 如果obj是损坏的弱对对象,返回#t,否则返回#f。
292 |
293 | **库**: (chezscheme)
294 |
295 | ```
296 | (bwp-object? #!bwp) => #t
297 | (bwp-object? 'bwp) => #f
298 |
299 | (define x (cons 'a 'b))
300 | (define p (weak-cons x '()))
301 | (set! x '*)
302 | (collect (collect-maximum-generation))
303 | (car p) => #!bwp
304 | (bwp-object? (car p)) => #t
305 | ```
306 |
307 |
308 |
309 | **过程**: (make-guardian)
310 |
311 | **返回**: 一个新的守护者
312 |
313 | **库**: (chezscheme)
314 |
315 |
316 | 守护者用程序来表示,封装注册的对象组,以保护它们。当守护者创建后,注册对象组为空。通过将对象作为参数传递给守护者,可以向守护者注册对象:
317 |
318 | ```
319 | (define G (make-guardian))
320 | (define x (cons 'aaa 'bbb))
321 | x => (aaa . bbb)
322 | (G x)
323 | ```
324 |
325 | 注册对象时,也可以指定"representative"对象,继续上面的示例:
326 |
327 | ```
328 | (define y (cons 'ccc 'ddd))
329 | y => (ccc . ddd)
330 | (G y 'rep)
331 | ```
332 |
333 | 与守护者关联的注册对象组在逻辑上分为两个不相交的子组:一个子组引用“可访问”对象,另一组引用“不可访问”对象。
334 | 不可访问对象是经过验证无法访问的对象(通过保护机制本身或通过弱对或星历对的car除外)。而可访问对象是未经证明的对象。“经过验证”一词在这里很重要:有可能在可访问组中某些对象确实不可访问,但这尚未得到证明。在某些情况下,直到对象实际上变得不可访问很久之后(在当前实现中,直到发生包含对象的世代的垃圾收集),才可能做出这种证明。
335 |
336 | 向守护者注册的对象最初被放置在可访问组中,并在它们变得不可访问后的某个时刻移入不可访问组。
337 | 不可访问组中的对象可通过调用不带参数的守护者来检索。如果不可访问组中没有对象,则守护者返回#f。继续上面的示例:
338 |
339 | ```
340 | (G) => #f
341 | (set! x #f)
342 | (set! y #f)
343 | (collect)
344 | (G) => (aaa . bbb) ; this might come out second
345 | (G) => rep ; and this first
346 | (G) => #f
347 | ```
348 |
349 | 对G的初始调用返回#f,因为绑定到x和y的对是向G注册的唯一对象,并且仍然可以通过这些绑定访问这些对。调用collect时,对象将移入不可访问的组。
350 | 因此,对G的两次调用将返回先前绑定到x的对和先前绑定到y的对的代表,尽管可能与所示顺序相反。(如上所述,对于弱对,如果对象已迁移到较早的一代,则调用collect实际上可能不足以证明该对象不可访问。)
351 |
352 | 即使一个未使用"representative"对象注册的,从守护者返回的对象被证明了无法访问(除了可能通过弱对或星历对的car字段),如果还未被存储管理系统回收,那么在删除了守护者系统内部或外部的最后一个非弱指针之前,不会回收它。
353 | 实际上,从守护者那里获取的对象在这方面或任何其他方面都没有特殊的地位。此功能避免了共享或循环结构可能会出现的问题。
354 | 由不可访问对象组成的共享或循环结构将被完整地保留,并且每个向守护者进行注册保存的片段都将放置在不可访问组里。
355 | 程序员可以完全控制结构体的处理顺序。
356 |
357 | 一个对象可以在守护者里多次注册,在这种情况下,可以多次检索该对象:
358 |
359 | ```
360 | (define G (make-guardian))
361 | (define x (cons 'aaa 'bbb))
362 | (G x)
363 | (G x)
364 | (set! x #f)
365 | (collect)
366 | (G) => (aaa . bbb)
367 | (G) => (aaa . bbb)
368 | ```
369 |
370 | 它也可以向多个守护者注册,并且守护者本身也可以向其他守护者注册。
371 |
372 | 如果一个对象没有使用"representative"对象向守护者注册,并且已经放入弱对或星历对的car字段,它将一直保留在car字段里,直到它从守护者返回并被其他程序丢弃,或者被守护者自身丢弃。
373 |
374 | ```
375 | (define G (make-guardian))
376 | (define x (cons 'aaa 'bbb))
377 | (define p (weak-cons x '()))
378 | (G x)
379 | (set! x #f)
380 | (collect)
381 | (set! y (G))
382 | y => (aaa . bbb)
383 | (car p) => (aaa . bbb)
384 | (set! y #f)
385 | (collect 1)
386 | (car p) => #!bwp
387 | ```
388 |
389 | (上面的第一个回收器将提升该对象到至少第一代,所以要求第二次回收器调用为第一代的回收,也可以通过多次调用collect来强制这样做。)
390 |
391 | 另一方面,如果指定"representative"对象(对象本身除外),被守护对象会从弱对或星历对的car字段被抛弃,与此同时,"representative"对象可以从守护者获得。
392 |
393 | ```
394 | (define G (make-guardian))
395 | (define x (cons 'aaa 'bbb))
396 | (define p (weak-cons x '()))
397 | (G x 'rep)
398 | (set! x #f)
399 | (collect)
400 | (G) => rep
401 | (car p) => #!bwp
402 | ```
403 |
404 | 下面的示例说明了在释放守护者本身时,已释放对象会将弱对的car字段设置为#!bwp:
405 |
406 | ```
407 | (define G (make-guardian))
408 | (define x (cons 'aaa 'bbb))
409 | (define p (weak-cons x '()))
410 | (G x)
411 | (set! x #f)
412 | (set! G #f)
413 | (collect)
414 | (car p) => #!bwp
415 | ```
416 |
417 | 下面的示例演示如何使用监护人来释放外部存储,例如由C库“ malloc”和“ free”操作管理的存储。
418 |
419 | ```
420 | (define malloc
421 | (let ([malloc-guardian (make-guardian)])
422 | (lambda (size)
423 | ; first free any storage that has been dropped. to avoid long
424 | ; delays, it might be better to deallocate no more than, say,
425 | ; ten objects for each one allocated
426 | (let f ()
427 | (let ([x (malloc-guardian)])
428 | (when x
429 | (do-free x)
430 | (f))))
431 | ; then allocate and register the new storage
432 | (let ([x (do-malloc size)])
433 | (malloc-guardian x)
434 | x))))
435 | ```
436 |
437 | `do-malloc`必须返回一个Scheme对象“header”,其中包含指向外部存储的指针(可能是一个无符号整数),并且所有对外部存储的访问都必须通过header进行。
438 | 特别是必须注意,删除相应的header之后,将没有指针指向Scheme以外的外部存储。`do-free`必须使用该封装的指针释放外部存储。
439 | 这两个原语都可使用`foreign-alloc`和`foreign-free`,或者使用C库里的"malloc"和"free"运算符(作为外部过程导入)来定义。(请参阅[第4章](https://cisco.github.io/ChezScheme/csug9.5/foreign.html#g22)。)
440 |
441 | 如果不希望等到调用malloc释放以前由malloc分配的已删除存储时,则可以使用collect-request处理程序来检查并释放已删除存储,如下所示。
442 |
443 | ```
444 | (define malloc)
445 | (let ([malloc-guardian (make-guardian)])
446 | (set! malloc
447 | (lambda (size)
448 | ; allocate and register the new storage
449 | (let ([x (do-malloc size)])
450 | (malloc-guardian x)
451 | x)))
452 | (collect-request-handler
453 | (lambda ()
454 | ; first, invoke the collector
455 | (collect)
456 | ; then free any storage that has been dropped
457 | (let f ()
458 | (let ([x (malloc-guardian)])
459 | (when x
460 | (do-free x)
461 | (f)))))))
462 | ```
463 |
464 | 只需进行一点重构,就可以将封装的外部地址注册为每个header的代表,在这种情况下,do-free将仅将外部地址作为参数。
465 |
466 |
467 |
468 | ## 第 13.3 节 锁定对象
469 |
470 | 从C变量或数据结构指向Scheme对象的所有指针,通常应在输入(或重新输入)Scheme之前丢弃。
471 | 如果无法遵循该准则,则可以通过锁对象或等效的C库过程Slock_object([第4.8节](https://cisco.github.io/ChezScheme/csug9.5/foreign.html#g34))锁定对象。
472 |
473 |
474 | **过程**: (lock-object obj)
475 |
476 | **返回**: unspecified
477 |
478 | **库**: (chezscheme)
479 |
480 |
481 | 锁定对象可防止存储管理器收回或重定位该对象。
482 | 应谨慎使用锁定,因为它会引入内存碎片并增加存储管理开销。
483 |
484 | 如果未解锁对象,锁定也会导致意外保留存储空间。
485 | 可以通过解锁对象或等效的C库过程Sunlock_object来解锁对象。
486 |
487 | 锁定立即值(例如,fixnum,布尔值和字符)或已设为静态的对象是不必要但无害的。
488 |
489 |
490 |
491 | **过程**: (unlock-object obj)
492 |
493 | **返回**: unspecified
494 |
495 | **库**: (chezscheme)
496 |
497 | 连续调用`lock-object`,`Slock_object`或同时调用这两个过程,可以多次锁定对象,在这种情况下,必须先通过相等次数的`unlock-object`或`Sunlock_object`的调用来将其解锁。
498 |
499 | 一个包含在锁定对象里的对象,比如已锁定的序对中的car对象,是无需锁定的,除非存在一个单独的指向该对象的指针。
500 | 也就是说,如果仅通过外部对象来间接访问内部对象,则应将其解锁,以便收集器在收集期间可以自由地重新放置它。
501 |
502 | 解锁立即值(例如,fixnum,布尔值和字符)或已设为静态的对象是不必要的,无效的但无害的。
503 |
504 |
505 |
506 | **过程**: (locked-object? obj)
507 |
508 | **返回**: 如果obj已经锁定,或者是立即值,或者是静态的,则返回#t
509 |
510 | **库**: (chezscheme)
511 |
512 |
513 | 如果收集器无法重新定位或回收obj,则该谓词将返回true,包括立即值,例如fixnums,布尔值和字符,以及已被设置为静态的对象。
514 |
515 |
516 |
--------------------------------------------------------------------------------
/14.ExpressionEditor.md:
--------------------------------------------------------------------------------
1 | # 第14章 表达式编辑器
2 |
3 | 如第2.2节所述,启用表达式编辑器(expeditor)后,允许用户去编辑输入到系统的表达式,并可以在输入表达式的历史记录中来回移动。
4 | 本章描述以下内容:一组参数,用来控制表达式编辑器的各方面行为(第14.1节);一个过程,用于绑定快捷键到编辑命令(第14.2节);各种内建编辑命令(第14.3节);创建新的编辑命令的机制(第14.4节)。
5 |
6 | 这些机制可以通过导入`expression-editor`模块来使用。
7 |
8 |
9 | **module**: `expression-editor`
10 |
11 | **libraries**: `(chezscheme)`
12 |
13 | 该`expression-editor`模块导出一组参数和过程的绑定,可以用于修改表达式编辑器与用户的交互方式,包括用于调用各种编辑命令的快捷键。
14 |
15 | 表达式编辑器的基本使用在[第2.2节]()进行了描述。
16 |
17 |
18 | ## 第14.1节 表达式编辑器的参数
19 |
20 | **全局参数**: `ee-auto-indent`
21 |
22 | `ee-auto-indent` 是一个布尔值,它确定表达式编辑器在输入表达式时是否缩进表达式。其默认值为#t。
23 |
24 |
25 | **全局参数**: `ee-standard-indent`
26 |
27 | `ee-standard-indent` 是一个非负 fixnum 值,它确定每个表达式相对于封闭表达式的缩进量(在单个空格中)。当`ee-auto-indent` 是 true,或显式调用缩进命令时,将根据某个缩进启发器使代码不对齐。该变量默认值是 2。
28 |
29 |
30 | **全局参数**: `ee-auto-paren-balance`
31 |
32 | `ee-auto-paren-balance` 是一个布尔值,确定在键入时,表达式编辑器是否自动更正右边的圆括号或方括号,以匹配左边的圆括号或方括号。默认值是 #t。
33 |
34 |
35 | **全局参数**: `ee-flash-parens`
36 |
37 | `ee-flash-parens` 是一个布尔值,它确定当输入一个括号,去匹配另一个括号时,表达式编辑器是否短暂移动光标。
38 |
39 |
40 | **全局参数**: `ee-paren-flash-delay`
41 |
42 | `ee-paren-flash-delay` 是一个非负 fixnum 值,它确定当光标移动到匹配的圆括号或方括号时,或当输入圆括号或方括号时,表达式编辑器暂停的时间(以毫秒为单位)。如果`ee-flash-parens`为false,则忽略该值。默认值是 100。
43 |
44 |
45 | *全局参数**: `ee-default-repeat`
46 |
47 | `ee-default-repeat` 是一个非负 fixnum 值,它确定当`ee-command-repeat` 编辑命令(默认绑定到Esc- ^ U)使用后,且后面没有数字序列的时候,下一个命令的重复执行次数。默认值是 4 。
48 |
49 |
50 | **全局参数**: `ee-noisy`
51 |
52 | `ee-noisy` 是布尔值,它确定当发生错误时,表达式编辑器是否发出哔哔声,例如尝试为非定界符找到匹配的定界符。默认值是 #f 。
53 |
54 |
55 | **全局参数**: `ee-history-limit`
56 |
57 | `ee-history-limit` 是一个非负 fixnum 值,它确定由表达式编辑器保存的,会话期间或会话之间的历史记录的数量。只有最后 `(ee-history-limit)` 条记录被保存。
58 |
59 |
60 | *全局参数**: `ee-common-identifiers`
61 |
62 | `ee-common-identifiers` 是一组符号列表,它们被认为足够通用了。它们可以出现在调用一个增量标识符的补全编辑命令之前,它们也可以在增量标示符的补全使用之后才出现。其默认值包含几十个条目。它们都有几个字符长度(根据用户最有可能完全输入短的理论)。
63 |
64 |
65 |
66 | ## 第14.2节 快捷键绑定
67 |
68 | 快捷键绑定通过`ee-bind-key`完成的。默认的快捷键绑定已在[第14.3节]()中描述。
69 |
70 | **过程**: `(ee-bind-key key procedure)`
71 |
72 | **返回**: unspecified
73 |
74 | 该`ee-bind-key`过程用于添加,改变被表达式编辑器认可的快捷键绑定。
75 |
76 | `key` 必须是字符或字符串,如果是字符串,它必须具备以下格式:
77 |
78 | ```
79 | => "+"
80 | ```
81 |
82 | 这里的如下格式:
83 |
84 | ```
85 | => \\e (指定一个转义字符)
86 | | ^x (指定 control-x)
87 | | \\^ (指定插入键)
88 | | \\\\ (指定反斜杠)
89 | | plain char (\或^以外的任何字符)
90 | ```
91 |
92 | 请注意,语法中的每个双反斜杠实际上仅表示字符串中的一个反斜杠。
93 |
94 | 例如,"\\eX"代表双字符的序列Escape-x,即“Escape”键,然后是(大写)“X”键。类似地,"\\e^X"代表两个字符的序列Escape-Control-x,即"escape"键,后跟Control-X。
95 |
96 | 由单个普通字符组成的字符键和字符串键始终表示单个击键。
97 |
98 | `procedure`参数通常应该是下面描述的内置编辑命令之一。也可以使用`ee-string-macro`和`ee-compose`定义新的编辑命令。
99 |
100 |
101 |
102 | ## 第14.3节 编辑命令
103 |
104 | 根据使用情况,编辑命令分为几部分。每个命令都列出了默认的字符序列,已经可以调用它的序列。
105 |
106 |
107 | ### 插入命令
108 |
109 | **命令**: `ee-insert-self`
110 |
111 | **快捷键**: 大多数可打印字符
112 |
113 | 插入键入的字符到条目中。
114 |
115 |
116 | **命令**: `ee-insert-paren`
117 |
118 | **快捷键**: (, ), [, ]
119 |
120 | 插入键入的圆括号或方括号到条目中。
121 | 如果参数`ee-auto-paren-balance`是true,当编辑器会匹配到左括号,且需要平衡左括号的时候,并会补充右括号。
122 |
123 | 如果参数`ee-flash-parens`是true, 编辑器会短暂移动光标到匹配的分隔符。如果找到一个,将暂停一会时间。该时间可以通过参数`ee-paren-flash-delay`来控制。如果当前没有显示匹配的分隔符,光标将视情况闪烁到条目显示部分的左上角或左下角。
124 |
125 | 如果用于圆括号或方括号以外的命令,则此命令的行为是不确定的。
126 |
127 |
128 | **命令**: `ee-newline`
129 |
130 | **快捷键**: 无
131 |
132 | 如果参数`ee-auto-indent`为true,则在光标位置插入换行符,移至下一行,并缩进该行。如果条目为空,则不执行任何操作。另请参阅`ee-newline/accept`。
133 |
134 |
135 | **命令**: `ee-open-line`
136 |
137 | **快捷键**: ^O
138 |
139 | 在光标位置插入换行符并缩进下一行,但不会移动到下一行。
140 |
141 |
142 | **命令**: `ee-yank-kill-buffer`
143 |
144 | **快捷键**: ^Y
145 |
146 | 插入kill缓冲区的内容,该内容由下面描述的删除命令设置。
147 |
148 |
149 | **命令**: `ee-yank-selection`
150 |
151 | **快捷键**: ^V
152 |
153 | 插入窗口系统当前选择或粘贴缓冲区的内容。在X Windows下的Shell窗口中运行时,此命令要求将DISPLAY环境变量设置为适当的显示。
154 |
155 |
156 |
157 | ### 光标移动命令
158 |
159 | **命令**: `ee-backward-char`
160 |
161 | **快捷键**: 左箭头, ^B
162 |
163 | 将光标向左移动一个字符。
164 |
165 |
166 | **命令**: `ee-forward-char`
167 |
168 | **快捷键**: 右箭头, ^F
169 |
170 | 将光标向右移动一个字符。
171 |
172 |
173 | **命令**: `ee-next-line`
174 |
175 | **快捷键**: 下箭头, ^N
176 |
177 | 将光标向下移动一行(如有必要,向左移动,以使光标不会超出最后一个可能的位置)。如果光标位于当前条目的末尾,并且尚未修改当前条目,则此命令的行为类似于`ee-history-fwd`。
178 |
179 |
180 | **命令**: `ee-previous-line`
181 |
182 | **快捷键**: 上箭头, ^P
183 |
184 | 将光标向上移动一行(必要时向左移动,以使光标不会超出最后一个可能的位置)。如果光标位于当前条目的顶部,并且尚未修改当前条目,则此命令的行为类似于`ee-history-bwd`。
185 |
186 |
187 | **命令**: `ee-beginning-of-line`
188 |
189 | **快捷键**: home, ^A
190 |
191 | 将光标移动到当前行的第一个字符。
192 |
193 |
194 | **命令**: `ee-end-of-line`
195 |
196 | **快捷键**: end, ^E
197 |
198 | 将光标移动到当前行最后一个字符的右侧。
199 |
200 |
201 | **命令**: `ee-beginning-of-entry`
202 |
203 | **快捷键**: escape-<
204 |
205 | 将光标移动到条目的第一个字符。
206 |
207 |
208 | **命令**: `ee-end-of-entry`
209 |
210 | **快捷键**: escape->
211 |
212 | 将光标移动到条目的最后一个字符的右侧。
213 |
214 |
215 | **命令**: `ee-goto-matching-delimiter`
216 |
217 | **快捷键**: escape-]
218 |
219 | 将光标移动到匹配的定界符。如果光标下方的字符不是括号或方括号,或者找不到匹配的定界符,则无效。
220 |
221 |
222 | **命令**: `ee-flash-matching-delimiter`
223 |
224 | **快捷键**: ^]
225 |
226 | 将光标短暂移动到匹配的定界符(如果可以找到),暂停一段时间,此时间由参数`ee-paren-flash-delay`控制。如果当前未显示匹配的定界符,则视情况将光标闪烁到条目显示部分的左上角或左下角。
227 |
228 |
229 | **命令**: `ee-exchange-point-and-mark`
230 |
231 | **快捷键**: ^X-^X
232 |
233 | 将光标移动到标记,并将标记保留在旧光标位置。 (可以使用ee-set-mark设置该标记。)
234 |
235 |
236 | **命令**: `ee-forward-sexp`
237 |
238 | **快捷键**: escape-^F
239 |
240 | 将光标移动到下一个表达式的开头。
241 |
242 |
243 | **命令**: `ee-backward-sexp`
244 |
245 | **快捷键**: escape-^B
246 |
247 | 将光标移动到上一个表达式的开头。
248 |
249 |
250 | **命令**: `ee-forward-word`
251 |
252 | **快捷键**: escape-f, escape-F
253 |
254 | 将光标移动到下一个单词的末尾。
255 |
256 |
257 | **命令**: `ee-backward-word`
258 |
259 | **快捷键**: escape-b, escape-B
260 |
261 | 将光标移动到上一个单词的开头。
262 |
263 |
264 | **命令**: `ee-forward-page`
265 |
266 | **快捷键**: pagedown, ^X-]
267 |
268 | 将光标向下移动一个屏幕页面。
269 |
270 |
271 | **命令**: `ee-backward-page`
272 |
273 | **快捷键**: pageup, ^X-[
274 |
275 | 将光标上移一个屏幕页面。
276 |
277 |
278 |
279 | ### 删除命令
280 |
281 |
282 | **命令**: `ee-delete-char`
283 |
284 | **快捷键**: delete
285 |
286 | 删除光标下方的字符。
287 | 另请参见ee-eof/delete-char。
288 |
289 |
290 | **命令**: `ee-backward-delete-char`
291 |
292 | **快捷键**: backspace (rubout), ^H
293 |
294 | 删除光标左侧的字符。
295 |
296 |
297 | **命令**: `ee-delete-line`
298 |
299 | **快捷键**: ^U
300 |
301 | 删除当前行的内容,留下空行。如果在多行条目中的仅显示的第一条上使用,`ee-delete-line`将会删除条目所有内容,类似`ee-delete-entry`(下面描述)。
302 |
303 |
304 | **命令**: `ee-delete-to-eol`
305 |
306 | **快捷键**: ^K, escape-K
307 |
308 | 如果光标在一行的末尾,则将该行与下一行连接,否则将其从光标位置删除到该行的末尾。
309 |
310 |
311 | **命令**: `ee-delete-between-point-and-mark`
312 |
313 | **快捷键**: ^W
314 |
315 | 删除当前光标位置和标记之间的文本。 (可以使用`ee-set-mark`设置该标记。)
316 |
317 |
318 | **命令**: `ee-delete-entry`
319 |
320 | **快捷键**: ^G
321 |
322 | 删除当前条目的内容。
323 |
324 |
325 | **命令**: `ee-reset-entry`
326 |
327 | **快捷键**: ^C
328 |
329 | 删除当前条目的内容并移至历史记录的末尾。
330 |
331 |
332 | **命令**: `ee-delete-sexp`
333 |
334 | **快捷键**: escape-^K, escape-delete
335 |
336 | 删除在光标下开始的表达式,或者如果没有表达式在光标下开始,则删除直到下一个表达式。
337 |
338 |
339 | **命令**: `ee-backward-delete-sexp`
340 |
341 | **快捷键**: escape-backspace (escape-rubout), escape-^H
342 |
343 | 删除光标左侧的表达式。
344 |
345 |
346 | **标识符/文件名补全命令**
347 |
348 | 这些命令执行标识符或文件名的补全。标识符补全在字符串常量之外执行。文件名补全是在字符串常量中执行的。(在确定光标是否在字符串常量内时,表达式编辑器仅查看当前行,因此可以被跨越多行的字符串常量所欺骗。)
349 |
350 |
351 | **命令**: `ee-id-completion`
352 |
353 | **快捷键**: 无
354 |
355 | 在光标左侧立即插入标识符或文件名可能补全的公共前缀。标识符的补全基于交互环境中定义的标识符。当恰好有一个可能的补全时,通用前缀是补全。如果没有文件名或标识符前缀紧接在光标的左边,或者可能的补全没有公共前缀,则此命令无效。如果连续运行两次,将显示可能补全的列表。
356 |
357 | 另请参见`ee-id-completion/indent`。
358 |
359 |
360 | **命令**: `ee-next-id-completion`
361 |
362 | **快捷键**: ^R
363 |
364 | 在光标左侧立即插入标识符或文件名的可能补全之一。标识符的补全基于交互环境中定义的标识符。如果连续运行两次或两次以上,此命令将循环遍历所有可能的补全。其遍历顺序由以下规则决定:首先出现的是标识符,其名称出现在参数`ee-common-identifiers`的列表值中;排第二的是绑定在交互环境中,但未绑定在scheme环境中的标识符(即用户定义的标识符)。最后出现的是scheme环境的那些标识符。出现在`ee-common-identifiers`列表中的一组匹配项中,最早列出的那些项显示在前。该顺序在其他两组中按字母顺序排列。
365 |
366 | 另请参见`ee-next-id-completion/indent`。
367 |
368 |
369 | ### 移动历史命令
370 |
371 | 表达式编辑器会在每个会话期间维护条目的历史记录。除非通过命令行参数`--eehistory off`禁用了此功能,否则它还会保存跨会话的历史记录。
372 |
373 | 从一个历史记录条目移动到另一个历史记录条目时,仅显示每个多行条目的第一行。redisplay命令(默认绑定^ L)可用于显示整个条目。也可以向下移动一行显示其余条目的一部分。
374 |
375 |
376 | **命令**: `ee-history-bwd`
377 |
378 | **快捷键**: escape-uparrow, escape-^P
379 |
380 | 如果当前条目为空或尚未修改,则移至上一个历史条目;否则无效。
381 | 另请参见`ee-previous-line`。
382 |
383 |
384 | **命令**: `ee-history-fwd`
385 |
386 | **快捷键**: escape-downarrow, escape-^N
387 |
388 | 如果当前条目为空或尚未修改,则移至下一个历史记录条目;否则无效。
389 | 另请参见`ee-next-line`。
390 |
391 |
392 | **命令**: `ee-history-bwd-prefix`
393 |
394 | **快捷键**: escape-p
395 |
396 | 移至最接近的上一个历史记录条目(如果有),该条目以构成当前条目的字符序列开头。可以多次使用以搜索相同的前缀。
397 |
398 |
399 | **命令**: `ee-history-fwd-prefix`
400 |
401 | **快捷键**: escape-n
402 |
403 | 移至最接近的下一个历史记录条目(如果有),该条目以构成当前条目的字符序列开头。可以多次使用以搜索相同的前缀。
404 |
405 |
406 | **命令**: `ee-history-bwd-contains`
407 |
408 | **快捷键**: escape-P
409 |
410 | 移动到最接近的上一个历史记录条目(如果有),其中包含组成当前条目的字符序列。可以多次使用以搜索相同的内容。
411 |
412 |
413 | **命令**: `ee-history-fwd-contains`
414 |
415 | **快捷键**: escape-N
416 |
417 | 移动到最接近的下一个历史记录条目(如果有),其中包含组成当前条目的字符序列。可以多次使用以搜索相同的内容。
418 |
419 |
420 |
421 | ### 缩进命令
422 |
423 |
424 | **命令**: `ee-indent`
425 |
426 | **快捷键**: escape-tab
427 |
428 | 重新缩进当前行。
429 | 另请参见`ee-next-id-completion/indent`。
430 |
431 |
432 | **命令**: `ee-indent-all`
433 |
434 | **快捷键**: escape-q, escape-Q, escape-^Q
435 |
436 | 重新缩进整个条目的每一行。
437 |
438 |
439 |
440 | ### 杂项命令
441 |
442 |
443 | **命令**: `ee-newline/accept`
444 |
445 | **快捷键**: enter, ^M
446 |
447 | 如果运行在一个开始于一个平衡的表达式的条目的后面,行为类似`ee-accept`,否则类似`ee-newline`。
448 |
449 |
450 | **命令**: `ee-id-completion/indent`
451 |
452 | **快捷键**: tab
453 |
454 | 如果标识符(在字符串常量之外)或文件名(在字符串常量内)刚出现在光标的左侧,并且刚输入了该标识符或文件名的最后一个字符,那么它的行为类似于`ee-id-completion`。否则它类似`ee-indent`。
455 |
456 | 如果已存在标识符或文件名(即不是刚刚键入的标识符)出现在光标的左侧,则该命令的首次使用类似于`ee-newline`,第二次连续使用类似于`ee-id-completion`,而第三次连续使用类似于`ee-id-completion`。
457 |
458 |
459 | **命令**: `ee-next-id-completion/indent`
460 |
461 | **快捷键**: none
462 |
463 | 如果标识符(在字符串常量之外)或文件名(在字符串常量内)出现在光标的左侧,并且刚输入了该标识符或标识符的最后一个字符,行为类似于ee-next-id-completion,否则行为类似ee-indent。
464 |
465 |
466 | **命令**: `ee-eof/delete-char`
467 |
468 | **快捷键**: ^D
469 |
470 | 如果条目为非空,则行为类似于`ee-delete-char`;否则行为类似`ee-eof`。如果该条目是非空的,并且此命令连续运行两次或更多次,则一旦该条目变为空,它就什么也不做。这是为了防止在重复运行命令(可能借助键盘的自动重复功能)以删除条目中的所有字符的情况下意外退出。
471 |
472 |
473 | ### 创建新编辑命令
474 |
475 | **过程**: `(ee-string-macro string)`
476 |
477 | **返回**: 一个新的编辑命令
478 |
479 | 新编辑命令在当前光标位置之前插入字符串。
480 |
481 | 预定义了两个字符串宏:
482 |
483 | ```
484 | (ee-string-macro "(define ") escape-d
485 | (ee-string-macro "(lambda ") escape-l
486 | ```
487 |
488 |
489 | **过程**: `(ee-compose ecmd ...)`
490 |
491 | **返回**: 一个新的编辑命令
492 |
493 | 每个ecmd必须是一个编辑命令。
494 | 新的编辑命令按顺序运行每个编辑命令ecmd。
495 |
496 | 例如,以下表达式将`^Xp`绑定到行为类似于`ee-history-bwd-prefix`的编辑命令,但是将光标留在表达式的末尾而不是第一行的末尾,从而能显示整个条目。
497 |
498 | ```
499 | (let ()
500 | (import expression-editor)
501 | (ee-bind-key "^Xp"
502 | (ee-compose ee-history-bwd ee-end-of-entry)))
503 | ```
504 |
505 | 类似`ee-id-completion`,作为复合命令的一部分运行时,即使连续运行两次,也无法识别它已经连续运行两次。
506 |
--------------------------------------------------------------------------------
/15.ThreadSystem.md:
--------------------------------------------------------------------------------
1 | 下面介绍Chez Scheme线程系统过程和语法形式。 除了锁,锁增量和锁减量之外,线程系统的功能在非基于Windows的系统上在Posix线程系统(pthreads)之上实现,并在基于Windows的系统上直接使用Windows API。 有关线程创建和交互的基本详细信息,请查阅系统上的相应文档。
2 |
3 | 大多数原生的Scheme过程都是*线程安全* 的,这意味着可以从多个线程中同时调用它们。 这包括诸如**cons**和**make-string**之类的分配操作,诸如**car**和**vector-ref**之类的访问器,诸如**+**和**sqrt**之类的数字运算符以及诸如**append**和**map**之类的非破坏性的高级原生操作。
4 |
5 | 简单的变动运算符(例如set-car!,vector-set!和record字段变动器)是线程安全的。 同样,对局部变量的分配,包括(未导出的)库和顶级程序变量的分配也是线程安全的。
6 |
7 | 大多数I/O操作应被视为具有破坏性,因为它们可能会修改端口的内部结构。
8 |
9 | 使用没有进行合适的同步策略的非线程安全的运算符可能会破坏它们所操作的对象。 这种损坏可能导致错误的行为,内存故障,甚至导致系统中止的不可恢复的错误。
10 |
11 | ### 线程创建
12 |
13 | > (fork-thread thunk)
14 |
15 | *thunk* 必须是一个接受0个参数的过程。
16 |
17 | **fork-thread**在一个新线程中调用*thunk* ,并返回一个线程对象。
18 |
19 | 除了打印它外,**fork-thread**返回的线程对象无法执行任何操作。
20 |
21 | 除了使用**fork-thread**以外,可通过外部代码来创建的线程必须在触及任何Scheme数据或调用任何Scheme过程之前调用**Sactivate_thread**
22 |
23 | > (thread? obj)
24 |
25 | 返回:如果*obj* 是一个线程对象,返回#t,否则返回#f
26 |
27 | > (get-thread-id)
28 |
29 | 返回:当前线程的id
30 |
31 | 线程id是由线程分配的线程号,并且与**get-process-id**返回的进程id没有关系,进程id在所有线程中都是相同的。
32 |
33 | ### 互斥锁
34 |
35 | > (make-mutex)
36 |
37 | 返回:一个新的互斥锁对象
38 |
39 | > (mutex? obj)
40 |
41 | 返回:如果*obj* 是互斥锁,返回#t
42 |
43 | > (mutex-acquire mutex) | (mutex-acquire mutex block?)
44 |
45 | *mutex* 必须是一个互斥锁。
46 |
47 | **mutex-acquire**获取由*mutex* 标识的互斥锁。可选的bool参数*block?* 默认为#t,指定线程是否应阻塞等待互斥锁。*block?* 如果省略或为true,则线程将阻塞,直到获取了互斥锁为止,并返回未指定的值。
48 |
49 | 如果*block?* 为false并且互斥锁当前已属于其他线程,当前线程并不会阻塞,而是,**mutex-acquire**立即返回值**#f**,以指示互斥锁不可用。 如果*block?* 为false并且成功获取了互斥锁,**mutex-acquire**返回#t。
50 |
51 | 互斥锁在Posix线程术语中是递归的,这意味着调用线程可以使用互斥锁获取(重新)获取它已经拥有的互斥锁(注:锁是可以重入的)。 在这种情况下,释放互斥锁需要执行相等数量的**mutex-release**。
52 |
53 | > (mutex-release mutex)
54 |
55 | *mutex* 必须是一个互斥锁。
56 |
57 | **mutex-release**释放由*mutex* 标识的互斥锁。 如果*mutex* 不属于调用线程(即释放别人的互斥锁),则会导致无法预料的行为。
58 |
59 | > (with-mutex mutex body1 body2 ...)
60 |
61 | **with-mutex**计算表达式*mutex* ,其必须可以计算为一个互斥锁,获取锁,并计算*body1 body2 ...* ,然后释放锁。无论body是正常返回还是通过控制操作(即可能由于错误而抛到continuation)释放互斥量,这都会导致**with-mutex**形式的非本地退出。如果控制随后通过continuation调用返回到body,则将重新获取互斥锁。
62 |
63 | 与直接使用**mutex-acquire**和**mutex-release**相比,使用**with-mutex**通常更方便,更安全。
64 |
65 | ### 条件
66 |
67 | > (make-condition)
68 |
69 | 返回:一个新的条件对象
70 |
71 | > (thread-condition? obj)
72 |
73 | 返回:如果*obj* 是条件对象,返回#t
74 |
75 | > (condition-wait cond mutex) | (condition-wait cond mutex timeout)
76 |
77 | 返回:如果调用线程被条件唤醒返回#t,如果调用线程超时等待返回#f
78 |
79 | *cond* 必须是条件对象,并且*mutex* 必须是互斥锁。 可选参数*timeout* 是类型为**time-duration**或**time-utc**或**#f**的时间记录,表示没有超时。 默认为**#f**。
80 |
81 | **condition-wait**等待由*cond* 标识的条件,且等待指定的*timeout*时长。在调用condition-wait时,调用线程必须已获取互斥锁*mutex* 。由于调用condition-wait的副作用而释放了*mutex* 。当稍后通过下述的过程之一从条件变量释放线程或超时到期时,将重新获取*mutex* ,并返回**condition-wait**。
82 |
83 | > (condition-signal cond)
84 |
85 | *cond* 必须是一个条件对象。
86 |
87 | **condition-signal**释放所有等待*cond* 标识的条件对象的线程的其中一个。
88 |
89 | > (condition-broadcast cond)
90 |
91 | *cond* 必须是一个条件对象。
92 |
93 | **condition-broadcast**释放所有等待*cond* 标识的条件对象的线程
94 |
95 | ### 锁
96 |
97 | 锁比互斥锁更原生,但是更加灵活和有效。
98 |
99 | 只要锁在进程共享的内存中分配,它们还可以独立于线程系统使用(包括在Chez Scheme的非线程版本中)以在分离的Scheme进程中执行同步操作。
100 | 锁只是一个字长的整数,即**iptr**或**uptr**外部类型,其中包含目标计算机的本机字节序,可能是使用**define-ftype**定义的大结构的一部分。必须在驻留于Scheme堆外部的内存中显式分配它,并在适当时显式释放。当仅涉及线程时(即,当不涉及多个进程时),可以通过**foreign-alloc**分配内存。 当涉及多个进程时,应在进程共享的某个区域中分配该锁。
101 |
102 | 使用**ftype-init-lock!** 初始化后,进程或线程可以尝试通过**ftype-lock!**或**ftype-spin-lock!**锁定*lock* 。一旦锁已被锁定并且在解锁之前,即使通过最近锁定它的进程或线程,再一次尝试锁定该锁也会失败。 任何进程或线程可以使用**ftype-unlock!**来解锁lock,而不仅仅是通过最近锁定锁的进程或线程来解锁。
103 |
104 | 锁机制提供的结构很少,并且分配和使用中的错误可能导致内存错误,死锁和其他问题。 因此,通常建议仅将锁用作更高级别抽象的一部分,以确保按规范方式使用锁。
105 | ```scheme
106 | (define lock
107 | (make-ftype-pointer uptr
108 | (foreign-alloc (ftype-sizeof uptr))))
109 |
110 | (ftype-init-lock! uptr () lock)
111 | (ftype-lock! uptr () lock) ⇒ #t
112 | (ftype-lock! uptr () lock) ⇒ #f
113 | (ftype-unlock! uptr () lock)
114 | (ftype-spin-lock! uptr () lock)
115 | (ftype-lock! uptr () lock) ⇒ #f
116 | (ftype-unlock! uptr () lock)
117 | ```
118 |
119 | > (ftype-init-lock! ftype-name (a ...) fptr-expr) | (ftype-init-lock! ftype-name (a ...) fptr-expr index)
120 |
121 | > (ftype-lock! ftype-name (a ...) fptr-expr) | (ftype-lock! ftype-name (a ...) fptr-expr index)
122 |
123 | > (ftype-spin-lock! ftype-name (a ...) fptr-expr) | (ftype-spin-lock! ftype-name (a ...) fptr-expr index)
124 |
125 | > (ftype-unlock! ftype-name (a ...) fptr-expr) | (ftype-unlock! ftype-name (a ...) fptr-expr index)
126 |
127 | 它们每个的语法都类似于**ftype-set!**,尽管带有隐式的*val-expr* 。 特别是,对*fptr-expr* 和访问器*a ...* 的限制和处理是相似的,但有一个重要的限制:最后一个访问器所指定的字段(该格式在其上进行操作)必须是一个字长的整数,即 ,**iptr,uptr**或具有本地字节序的等效项。
128 |
129 | **ftype-init-lock!**应该在使用任何其他运算符之前用于初始化锁; 如果不这样做,则其他操作符的行为是不确定的。
130 |
131 | **ftype-lock!** 可用于加锁。如果发现在操作时锁已解锁,则将其锁定并返回#t;如果发现该锁已加锁,则返回#f且不更改该锁。
132 |
133 | **ftype-spin-lock!** 也可以用来加锁。 如果在操作时发现该锁已解锁,则将其锁定并返回; 如果发现该锁已锁定,它将一直等待直到锁被解锁,然后再锁定并返回。如果没有其他进程或者线程来释放该锁,该操作不会返回且不能以正常的方式(包括GC)中断。也不保证公平,因此即使其他进程正在主动锁定和释放该锁,进程也可能无限期挂起。
134 |
135 | **ftype-unlock!** 用于解锁。如果发现锁已被锁定,则将其解锁并返回。 否则,它将返回而不更改锁。
136 |
137 | ### 原子操作Locked increment and decrement
138 |
139 | 当需要原子递增或递减时,可以使用此处描述的锁定操作。
140 |
141 | > (ftype-locked-incr! ftype-name (a ...) fptr-expr) | (ftype-locked-incr! ftype-name (a ...) fptr-expr index)
142 |
143 | 返回:如果更新的值为0,则为#t,否则为#f
144 |
145 | > (ftype-locked-decr! ftype-name (a ...) fptr-expr) | (ftype-locked-decr! ftype-name (a ...) fptr-expr index)
146 |
147 | 返回:如果更新的值为0,则为#t,否则为#f
148 |
149 | 它们每个的语法都类似于**ftype-set!**,尽管带有隐式的*val-expr* 。 特别是,对*fptr-expr* 和访问器*a ...* 的限制和处理是相似的,但有一个重要的限制:最后一个访问器所指定的字段(该格式在其上进行操作)必须是一个字长的整数,即 ,**iptr,uptr**或具有本地字节序的等效项。
150 |
151 | **ftype-locked-incr!** 自动读取指定字段的值,将值加1,然后将新值写回该字段。 同样,**ftype-locked-decr!** 原子读取指定字段的值,从该值中减去1,然后将新值写回到该字段中。 如果新值为0,则两者都返回#t,否则返回#f。
152 |
153 | ### 引用计数
154 |
155 | 在Scheme堆之外管理内存的应用程序可以利用Scheme存储管理系统通过*ftype guardians* 执行引用计数。 在引用计数的内存管理系统中,每个对象都保存着指向它的指针计数。 当创建一个新的指针时,该计数增加;而在删除指针时,该计数减小。 当计数达到零时,不再需要该对象,并且可以将其占用的内存用于其他目的。
156 |
157 | > (ftype-guardian ftype-name)
158 |
159 | *ftype-name* 必须命名一个ftype。 ftype的第一个基本字段(或在unions的情况下为第一个基本字段)必须是具有本地尾数的字长整数(iptr或uptr)。 假定此字段保存引用计数。
160 |
161 | 返回新的ftype guardian *g* ,可以使用其注册*ftype-name* 类型(或*ftype-nam* e的某些子类型)的ftype-pointer。 通过使用ftype指针作为参数调用*g* ,可以向*g* 注册ftype指针。
162 |
163 | ftype guardian不会像普通的guardian那样自动保护其注册的ftype指针免遭回收。而是,对于每个通过普通(非弱,非监护人指针)变得不可访问的已注册的ftype指针,guardian会减少ftype指针指向的对象的引用计数。如果引用计数值为0,则ftype指针将保留并可以从guardian中检索。 但是,如果生成的引用计数值非零,则不会保留ftype指针。假设回收器外部的代码正确维护了引用计数,则从ftype guardian检索的对象(通过不带参数的调用)将确保具有0引用计数。回收器使用等效的**ftype-locked-decr!**来减少引用计数,以支持由多个进程共享的内存中的非Scheme对象。在这样的系统中,程序本身应使用**ftype-locked-incr!** 和**ftype-locked-decr!** 或非Scheme等效项(例如,第4.8节中所述C语言的scheme.h中的 LOCKED_INCR和LOCKED_DECR宏)来维护引用计数。
164 |
165 | 下面的示例为ftype对象定义了一个简单的ftype和一个分配器,该对象释放以前分配的且不再可访问的ftype对象。
166 | ```scheme
167 | module (A make-A free-dropped-As)
168 | (define-ftype A
169 | (struct
170 | [refcount uptr]
171 | [data int]))
172 | (define g (ftype-guardian A))
173 | (define free-dropped-As
174 | (lambda ()
175 | (let ([a (g)])
176 | (when a
177 | (printf "freeing ~s\n" (ftype-ref A (data) a))
178 | (foreign-free (ftype-pointer-address a))
179 | (free-dropped-As)))))
180 | (define make-A
181 | (lambda (n)
182 | (free-dropped-As)
183 | (let ([a (make-ftype-pointer A (foreign-alloc (ftype-sizeof A)))])
184 | (ftype-set! A (refcount) a 1)
185 | (ftype-set! A (data) a n)
186 | (g a)
187 | a))))
188 | ```
189 |
190 | 我们可以通过分配,丢弃并立即回收指向A的ftype指针进行测试。
191 | ```scheme
192 | > (do ([i 10 (fx- i 1)])
193 | ((fx= i 0))
194 | (make-A i)
195 | (collect))
196 | freeing 10
197 | freeing 9
198 | freeing 8
199 | freeing 7
200 | freeing 6
201 | freeing 5
202 | freeing 4
203 | freeing 3
204 | freeing 2
205 | > (free-dropped-As)
206 | freeing 1
207 | ```
208 |
209 | 由ftype guardian保护的对象可能包含指向其他对象的指针,这些其他对象的引用计数也应在分配包含对象时增加,并在释放包含对象时减少。
210 |
211 | ### 线程参数
212 |
213 | > (make-thread-parameter object) | (make-thread-parameter object procedure)
214 |
215 | 创建线程参数后,将在每个当前线程和将来的线程中放置一个单独的位置,以保存参数的内部状态变量的值。 (当该参数变得不可访问时,存储管理器可以消除该位置。)一个线程中对线程参数的更改不会被其他任何线程看到。
216 |
217 | 创建新线程时(请参阅**fork-thread**),每个线程参数的当前值(而非位置)都由新线程从派生线程继承。 类似地,当第一次激活通过其他方式创建的线程时(请参见4.8节中的**Sactivate_thread**),每个线程参数的当前值(而非位置)都由新线程从主(原始)线程继承。
218 |
219 | 大多数内置参数是线程参数,但有些是全局的。 在他们定义的地方已经标注了是线程的还是全局的。 在非线程版本的Chez Scheme中,内置全局参数和线程参数之间没有区别。
220 |
221 | ### I/O 缓冲区
222 |
223 | Chez Scheme为提高效率而缓冲文件I/O操作,但是缓冲的I/O不是线程安全的。 两个线程并发地读写同一个缓冲端口可能会破坏该端口,从而导致缓冲区溢出,并最终导致无效的内存引用。
224 |
225 | 当以缓冲模式**none**打开时,可以禁用二进制输出端口上的缓冲。 但是,由于需要支持先行(lookahead),因此无法完全禁用输入端口上的缓冲,并且由于要在字符和字节之间进行转换的代码转换器有时需要先行输入,因此无法完全禁用文本端口(甚至是文本输出端口)上的缓冲。
226 |
227 | 因此,除非在二进制输出端口开启缓冲模式为**none**的特殊情况下,否则两个线程绝不应该并发地读写同一个端口。替代方法包括指定一个线程为给定端口执行所有I / O,和为每个线程提供通用端口包装程序,仅在获取互斥量后才将请求转发到端口。
228 |
229 | 初始的控制台以及当前的输入和输出端口以及transcript端口都是线程安全的,因此多个线程向控制台打印错误和/或调试消息是安全的。即使在同一行内,输出也可能是交错的,但是端口不会损坏。 这些端口的线程安全性是通过为每个I/O操作获取一个互斥锁的高成本来实现的。
230 |
231 | ### 实例:有界队列
232 |
233 | 以下代码摘自文章“A Scheme for native threads[^1]”,它使用许多线程系统功能实现了有界队列。 有界队列具有固定数量的可用插槽。 当队列已满时尝试入队会导致调用线程阻塞。 尝试从空队列中出队会导致调用线程阻塞。
234 | ```scheme
235 | (define-record-type bq
236 | (fields
237 | (immutable data)
238 | (mutable head)
239 | (mutable tail)
240 | (immutable mutex)
241 | (immutable ready)
242 | (immutable room))
243 | (protocol
244 | (lambda (new)
245 | (lambda (bound)
246 | (new (make-vector bound) 0 0 (make-mutex)
247 | (make-condition) (make-condition))))))
248 |
249 | (define dequeue!
250 | (lambda (q)
251 | (with-mutex (bq-mutex q)
252 | (let loop ()
253 | (let ([head (bq-head q)])
254 | (cond
255 | [(= head (bq-tail q))
256 | (condition-wait (bq-ready q) (bq-mutex q))
257 | (loop)]
258 | [else
259 | (bq-head-set! q (incr q head))
260 | (condition-signal (bq-room q))
261 | (vector-ref (bq-data q) head)]))))))
262 |
263 | (define enqueue!
264 | (lambda (item q)
265 | (with-mutex (bq-mutex q)
266 | (let loop ()
267 | (let* ([tail (bq-tail q)] [tail^ (incr q tail)])
268 | (cond
269 | [(= tail^ (bq-head q))
270 | (condition-wait (bq-room q) (bq-mutex q))
271 | (loop)]
272 | [else
273 | (vector-set! (bq-data q) tail item)
274 | (bq-tail-set! q tail^)
275 | (condition-signal (bq-ready q))]))))))
276 |
277 | (define incr
278 | (lambda (q i)
279 | (modulo (+ i 1) (vector-length (bq-data q)))))
280 |
281 | ```
282 | 下面的代码演示了有界队列在一组线程中的应用,这些线程分别扮演数据的消费者和生产者。
283 |
284 | ```scheme
285 | (define job-queue)
286 | (define die? #f)
287 |
288 | (define make-job
289 | (let ([count 0])
290 | (define fib
291 | (lambda (n)
292 | (if (< n 2)
293 | n
294 | (+ (fib (- n 2)) (fib (- n 1))))))
295 | (lambda (n)
296 | (set! count (+ count 1))
297 | (printf "Adding job #~s = (lambda () (fib ~s))\n" count n)
298 | (cons count (lambda () (fib n))))))
299 |
300 | (define make-producer
301 | (lambda (n)
302 | (rec producer
303 | (lambda ()
304 | (printf "producer ~s posting a job\n" n)
305 | (enqueue! (make-job (+ 20 (random 10))) job-queue)
306 | (if die?
307 | (printf "producer ~s dying\n" n)
308 | (producer))))))
309 |
310 |
311 | (define make-consumer
312 | (lambda (n)
313 | (rec consumer
314 | (lambda ()
315 | (printf "consumer ~s looking for a job~%" n)
316 | (let ([job (dequeue! job-queue)])
317 | (if die?
318 | (printf "consumer ~s dying\n" n)
319 | (begin
320 | (printf "consumer ~s executing job #~s~%" n (car job))
321 | (printf "consumer ~s computed: ~s~%" n ((cdr job)))
322 | (consumer))))))))
323 |
324 |
325 |
326 | (define (bq-test np nc)
327 | (set! job-queue (make-bq (max nc np)))
328 | (do ([np np (- np 1)])
329 | ((<= np 0))
330 | (fork-thread (make-producer np)))
331 | (do ([nc nc (- nc 1)])
332 | ((<= nc 0))
333 | (fork-thread (make-consumer nc))))
334 |
335 |
336 | ```
337 | 这是示例程序运行后的可能的前几行输出:
338 | ```scheme
339 | > (begin
340 | (bq-test 3 4)
341 | (system "sleep 3")
342 | (set! die? #t))
343 | producer 3 posting a job
344 | Adding job #1 = (lambda () (fib 29))
345 | producer 3 posting a job
346 | Adding job #2 = (lambda () (fib 26))
347 | producer 3 posting a job
348 | Adding job #3 = (lambda () (fib 22))
349 | producer 3 posting a job
350 | Adding job #4 = (lambda () (fib 21))
351 | producer 2 posting a job
352 | Adding job #5 = (lambda () (fib 29))
353 | producer 1 posting a job
354 | Adding job #6 = (lambda () (fib 29))
355 | consumer 4 looking for a job
356 | producer 3 posting a job
357 | Adding job #7 = (lambda () (fib 24))
358 | consumer 4 executing job #1
359 | consumer 3 looking for a job
360 | producer 2 posting a job
361 | Adding job #8 = (lambda () (fib 26))
362 | consumer 3 executing job #2
363 | consumer 3 computed: 121393
364 | consumer 3 looking for a job
365 | producer 1 posting a job
366 | Adding job #9 = (lambda () (fib 26))
367 | ...
368 | ```
369 |
370 | 在“A Scheme for native threads[^1]”中给出了其他示例,包括可悬挂线程的定义和在无法访问时自动终止的线程。
371 |
372 |
373 |
374 | [^1]: R. Kent Dybvig. A Scheme for native threads. In Symposium in Honor of Mitchell Wand, August 2009. http://www.ccs.neu.edu/events/wand-symposium/.
375 |
--------------------------------------------------------------------------------
/16.CompatibilityFeatures.md:
--------------------------------------------------------------------------------
1 | # 第16章 兼容功能 (Compatibility Features)
2 | 本章介绍了当前版本的 Chez Scheme中包含的几个项目,主要是为了与较早版本的系统兼容。
3 |
4 | [16.1节](#16.1) 描述了一个哈希表接口,此接口从现在起被 R6RS哈希表接口所取代。[16.2节](#16.2) 描述了扩展语法宏。当前版本的 Chez Scheme直接支持这些功能,但在将来的版本中可能不再支持。新程序应改用 The Scheme Programming Language,第4版 [11]中描述的标准机制。
5 |
6 | [16.3节](#16.3) 描述了一种将类似记录的结构定义为向量而不是新的唯一类型的机制。新程序应改用 `define-record` (在[7.15节]((../CSUG/07.OperationsOnObjects.html#7.15))中介绍)。
7 |
8 | [16.4节](#16.4) 描述了与 Chez Scheme一起分发的兼容性文件,其中包含 Chez Scheme不再直接支持的表单和过程的定义。
9 |
10 | 16.1节 哈希表 (Hash Tables)
11 | 这里的哈希表过程被[第7.12节](../CSUG/07.OperationsOnObjects.html#7.12)中列出的新哈希表过程所取代 (原文是 "be obviated")。
12 |
13 | 过程:`(make-hash-table)`
14 |
过程:`(make-hash-table weak?)`
15 |
返回:一个新的哈希表
16 |
所在库:(chezscheme)
17 |
18 | 如果使用了 `weak?`参数且该参数不是 `#f`,那么哈希表是弱哈希表,这意味着它不保护密钥不受垃圾收集器的影响。垃圾回收器回收的键将从表中删除,它们的关联值将在下次修改表时(如果不是更早的话)被删除。
19 |
20 | 过程:`(hash-table? obj)`
21 |
返回:如果 `obj`是哈希表,则返回 `#t`,否则返回 `#f`
22 |
所在库:(chezscheme)
23 |
24 | 过程:`(put-hash-table! ht k v)`
25 |
返回:未指定的
26 |
所在库:(chezscheme)
27 |
28 | ht必须是哈希表。k和 v可以是任何 Scheme值。
29 |
30 | `put-hash-table!` 会在 ht中关联值 v与键 k
31 |
32 | 过程:`(get-hash-table ht k d)`
33 |
返回:参见以下
34 |
所在库:(chezscheme)
35 |
36 | `get-hash-table`返回 ht中与 k关联的值。如果 ht中没有与 k相关联的值,则 `get-hash-table`返回默认值 d。
37 |
38 | 键之间通过 `eq?`进行比较。
39 |
40 | 因为对象可能被垃圾收集器移动,所以 `get-hash-table` 可能需要重新哈希一些对象,因此在哈希表中引起副作用。因此,使用 `get-hash-table`从多个线程对同一哈希表进行并发访问是不安全的。
41 |
42 | 过程:`(remove-hash-table! ht k)`
43 |
返回:未指定的
44 |
所在库:(chezscheme)
45 |
46 | `remove-hash-table!`从 ht中删除任何与 k的关联。
47 |
48 | 过程:`(hash-table-map ht p)`
49 |
返回:参见以下
50 |
所在库:(chezscheme)
51 |
52 | `hash-table-map`将 p应用于每个键,以 ht的值关联(不按特定顺序),并再次以不特定的顺序返回结果值的列表。p接受两个参数,一个键和一个值。
53 |
54 | 过程:`(hash-table-for-each ht p)`
55 |
返回:未指定的
56 |
所在库:(chezscheme)
57 |
58 | 每个哈希表将 p应用于每个键,以 ht表示的值关联,没有特定的顺序。与 `hash-table-map`不同,它不创建值列表;相反,它的值是不确定的。p接受两个参数,一个键和一个值。
59 |
60 | 16.2节 扩展语法宏 (Extend-Syntax Macros)
61 | 本节介绍了 `extend-syntax`,这是一种功能强大且易于使用的基于 模式匹配[27]。使用 `extend-syntax`编写的语法转换与使用 `define-syntax`和 `syntax-case`编写的语法转换 相似,不同之处在于,`extend-syntax`产生的转换不会自动遵循词法作用域。
62 |
63 | 通常不可能无缝地将使用语法大小写编写的语法抽象与使用扩展语法编写的语法抽象混合在一起。通常优选在任何可能的地方使用一个或另一个。在语法用例扩展器中对扩展语法的支持仅是作为迁移到语法用例的辅助。
64 |
65 | 语法:`(extend-syntax (name key ...) (pat fender template) ...)`
66 |
返回:未指定的
67 |
所在库:(chezscheme)
68 |
69 | 标识符名称是要定义的语法扩展名的名称或语法关键字。当系统扩展器处理 `car`为 name的任何列表表达式时,将对此表达式调用由 `extend-syntax`生成的语法转换过程。剩余的标识符 `key ...`是在展开期间要在输入表达式中识别的附加关键字(例如 `cond`或` case`中的 `else`)。
70 |
71 | key列表之后的每个子句都包含一个模式 `pat`,一个可选的挡板 `fender`和模板 `template`。可选的 `fender`经常被省略。`pat`指定要选择的子句的输入表达式必须具有的语法。模式中不是关键字(即模式 `pat`中的变量)的标识符绑定到输入表达式的相应部分。如果存在的话,`fender`是一个 Scheme表达式,它指定输入表达式(通过模式变量访问)上必须满足的附加约束,以便选择子句。模板指定输出的形式,通常是根据模式变量。
72 |
73 | 在扩展过程中,转换过程 `extend-syntax`会生成尝试以子句给定的顺序将输入表达式与每个模式进行匹配。如果输入表达式与模式匹配,则将模式变量绑定到输入表达式的相应部分,并对子句的 `fender`(如果有的话)进行求值。如果 `fender`返回一个真值,则执行给定的扩展。如果输入与模式不匹配或 `fender`返回错误值,则转换过程将尝试下一个子句。如果无法选择任何子句,则会引发条件类型 `&assertion`的异常。
74 |
75 | 在模式中,省略号(...)可用于指定前一个模式片段或原型的零次或多次出现。类似地,可以在输出中使用省略号来指定零个或多个扩展原型的构造。在这种情况下,扩展原型必须包含输入模式原型的一部分。模式、模板、模板内的省略号和挡板的使用将在下面的一系列示例中进行说明。
76 |
77 | 第一个例子,定义 `rec`,用了一个关键字 `rec`,一个没有 `fender`的子句,并且没使用省略号。
78 |
79 | ```
80 | (extend-syntax (rec)
81 | [(rec id val)
82 | (let ([id #f])
83 | (set! id val)
84 | id)] )
85 | ```
86 | 第二个例子,定义 `when`,展示了省略号的用法。
87 | ```
88 | (extend-syntax (when)
89 | [(when test exp1 exp2 ...)
90 | (if test (begin exp1 exp2 ...) #f)])
91 | ```
92 | 下一个示例显示了 `let`。`let`的定义显示了多个省略号的使用,其中一个用于标识符/值的序对,另一个用于主体中的表达式。它还表明原型不必是单个标识符,并且原型的各个部分可以在模板中彼此分开。
93 | ```
94 | (extend-syntax (let)
95 | [(let ([x e] ...) b1 b2 ...)
96 | ((lambda (x ...) b1 b2 ...) e ...)])
97 | ```
98 | 下一个示例显示 `let*`,其语法与 `let`相同,但它是用两个子句(一个用于基本情况,一个用于递归步骤)递归定义的,因为它必须生成嵌套结构。
99 | ```
100 | (extend-syntax (let*)
101 | [(let* () b1 b2 ...)
102 | (let () b1 b2 ...)]
103 | [(let* ([x e] more ...) b1 b2 ...)
104 | (let ([x e]) (let* (more ...) b1 b2 ...))])
105 | ```
106 | 第一个模式/模板序对会匹配没有标识符/值序对的任何 `let*`表达式,并将其映射到等效的 `begin`表达式中。这是基本情况。第二个模式/模板序对将任何 `let*`表达式与一个或多个标识符/值序对匹配,并将其转换为绑定第一个序对的 `let`表达式,主体的主体是 `let*`表达式,其绑定其余序对。这是递归步骤,最终将我们引向基本情况,因为我们在每一步都删除了一个标识符/值序对。请注意,第二个模式更多地使用了模式变量用于第二和以后的标识符/值序对;这样可以减少模式和模板的混乱程度,并且可以清楚地看到只有第一个标识符/值序对被明确处理。
107 |
108 | `and`的定义需要三个子句。第一个子句是识别`(and)`所必需的,后两个子句递归地定义其他 `and`的形式。
109 | ```
110 | (extend-syntax (and)
111 | [(and) #t]
112 | [(and x) x]
113 | [(and x y ...) (if x (and y ...) #f)])
114 | ```
115 | `cond` 的定义需要四个子句。与 `let*`一样,必须对 `cond`进行递归描述,部分原因是它会产生嵌套的 `if`表达式,部分原因是一个省略号原型不足以描述所有可能的 `cond`子句。`cond`的定义还要求除了 `cond`之外,还要将 `else`指定为关键字。这是定义:
116 | ```
117 | (extend-syntax (cond else)
118 | [(cond) #f]
119 | [(cond (else e1 e2 ...))
120 | (begin e1 e2 ...)]
121 | [(cond (test) more ...)
122 | (or test (cond more ...))]
123 | [(cond (test e1 e2 ...) more ...)
124 | (if test
125 | (begin e1 e2 ...)
126 | (cond more ...))])
127 | ```
128 | 其中两个子句是基本情况,两个子句是递归步骤。第一个基本情况是空的条件。在这种情况下,`cond`的值是不确定的,因此 #f的选择 有些随意。第二种基本情况是只包含 else子句的 `cond`;它将转换为等效的 begin表达式。两个递归步骤在 `cond` 子句中的表达式数量不同。当第一个 true子句仅包含测试表达式时,`cond`的值就是测试的值。这类似于或做什么,因此我们将 `cond`子句扩展为 `or`或表达。另一方面,当测试表达式之后有表达式时,将返回最后一个表达式的值,因此我们使用 if和 begin。
129 |
130 | 为了使 `let`的语法绝对正确,我们实际上必须要求输入中的绑定标识符 `x ...`是符号。如果我们输入类似 `(let ([3 x]) x)`的内容,而没有在 `let`中得到错误,是因为它没去检查验证标识符位置中的对象是否为符号; 那么,`lambda`可能会抱怨,或者可能是扩展完成很久之后系统的求值器要抱怨。所以这就是挡板的好处。
131 | ```
132 | (extend-syntax (let)
133 | [ (let ([x e] ...) b1 b2 ...)
134 | (andmap symbol? '(x ...))
135 | ((lambda (x ...) b1 b2 ...) e ...) ] )
136 | ```
137 | `andmap`相当于 `(compose and map)`, 使用 `andmap`检查 `'(x ...)`, 确保每个绑定标识符都是一个符号。`fender`只是 Scheme表达式。在该表达式中,首先使用与子句的模板部分相同的规则来扩展任何引用的对象。在这种情况下,`'(x ...)`被扩展到标识符/值对中的标识符列表。
138 |
139 | 扩展语法通常可以处理您需要的所有内容,但是某些语法扩展定义要求能够包含评估任意 Scheme表达式的结果。此功能由提供。
140 |
141 | 语法:`(with ((pat expr)...) template)`
142 | 返回:已处理的模板
143 |
144 | `with`仅在扩展语法内的模板内有效。`with`模式与 `extend-syntax`模式相同,`with`表达式与 `extend-syntax`fender相同,`with`模板与 `extend-syntax`模板相同。
145 |
146 | 可以使用 `with`将新的模式标识符绑定到扩展语法模板中由任意方案表达式生成的表达式。也就是说,`with`可以从扩展语法的声明式样式转为完整的 Scheme过程样式。
147 |
148 | `with`的一个常见用法是在模板中引入临时标识符或临时标识符列表。`with`还用于执行复杂的转换,如果在扩展语法框架中执行,转换可能会很笨拙或效率低下。
149 |
150 | 例如,或要求使用临时标识符。我们可以定义或如下。
151 | ```
152 | (extend-syntax (or)
153 | [(or) #f]
154 | [(or x) x]
155 | [(or x y ...)
156 | (let ([temp x])
157 | (if temp temp (or y ...)))])
158 | ```
159 | 这种方法工作正常,直到我们将或表达式放置在 temp发生的范围之前,在这种情况下,可能会发生奇怪的事情,因为 `extend-syntax`不遵循词法检查。(这是 `define-syntax`优于 `extend-syntax`的原因之一。)
160 | ```
161 | (let ([temp #t])
162 | (or #f temp)) => #f
163 | ```
164 | 一种解决方案是使用 `gensym`和 `with`创建临时标识符,如下所示。
165 | ```
166 | (extend-syntax (or)
167 | [(or) #f]
168 | [(or x) x]
169 | [(or x y ...)
170 | (with ([temp (gensym)])
171 | (let ([temp x])
172 | (if temp temp (or y ...))))])
173 | ```
174 | 同样,`with`可以用来以扩展语法无法直接实现的方式组合输入模式的元素,例如以下折叠加示例。
175 | ```
176 | (extend-syntax (folding-plus)
177 | [(folding-plus x y)
178 | (and (number? 'x) (number? 'y))
179 | (with ([val (+ 'x 'y)])
180 | val)]
181 | [(folding-plus x y) (+ x y)])
182 | ```
183 | 如果 x和 y均为数字常量,则 `folding-plus`折叠为`(+ x y)`的值。否则,`folding-plus`将转换为`(+ x y)`以供以后评估。fender在扩展时检查操作数是否为数字,`with`执行求值。与 fender一样,扩展仅在带引号的表达式内执行,因为 `quote`将数据与 Scheme表达式的其余部分区分开。
184 |
185 | 下面的示例利用了允许我们将模式绑定到表达式的事实,将模式变量列表绑定到临时符号列表。此临时列表帮助我们实现 `sigma`句法扩展。`sigma`与 `lambda`相似,除了它在标识符列表中分配标识符而不是创建新的绑定。它可以用来并行执行一系列分配。
186 | ```
187 | (extend-syntax (sigma)
188 | [(sigma (x ...) e1 e2 ...)
189 | (with ([(t ...) (map (lambda (x) (gensym)) '(x ...))])
190 | (lambda (t ...)
191 | (set! x t) ...
192 | e1 e2 ...))])
193 |
194 | (let ([x 'a] [y 'b])
195 | ((sigma (x y) (list x y)) y x)) => (b a)
196 | ```
197 | 下面的最后一个示例使用扩展语法来实现 `define-structure`,这与 Scheme编程语言第4版第8.4节中使用语法 `case`的类似示例相同。
198 |
199 | `define-structure` 的定义使用了两个 pattern / template子句。需要两个子句来处理第二个子表达式的可选性。第一个子句与没有第二个子表达式的形式匹配,仅将其转换为存在第二个子表达式的等效形式,但为空。
200 |
201 | 该定义还大量使用 `with`在展开时计算方案表达式。前四个 `with`子句用于生成标识符,这些标识符命名自动定义的过程。(过程格式在这里特别有用,但是可以用 `string-append!`替换,根据需要使用 `symbol->string`。)前两个子句生成单个标识符(用于构造函数和谓词),而后两个子句生成标识符列表(用于字段访问和赋值过程)。第五个 `with`子句(外部 with中的最后一个子句)用于计算结构的每个实例所需的总长度向量,其中必须包括名称和所有字段的空间。最后的 `with`子句(内部 `with`中的唯一子句)用于创建向量索引列表,每个字段一个索引(从1开始,因为结构名称占据位置0)。
202 | ```
203 | (extend-syntax (define-structure)
204 | [(define-structure (name id1 ...))
205 | (define-structure (name id1 ...) ())]
206 | [(define-structure (name id1 ...) ([id2 val] ...))
207 | (with ([constructor
208 | (string->symbol (format "make-~a" 'name))]
209 | [predicate
210 | (string->symbol (format "~a?" 'name))]
211 | [(access ...)
212 | (map (lambda (x)
213 | (string->symbol
214 | (format "~a-~a" 'name x)))
215 | '(id1 ... id2 ...))]
216 | [(assign ...)
217 | (map (lambda (x)
218 | (string->symbol
219 | (format "set-~a-~a!" 'name x)))
220 | '(id1 ... id2 ...))]
221 | [count (length '(name id1 ... id2 ...))])
222 | (with ([(index ...)
223 | (let f ([i 1])
224 | (if (= i 'count)
225 | '()
226 | (cons i (f (+ i 1)))))])
227 | (begin
228 | (define constructor
229 | (lambda (id1 ...)
230 | (let* ([id2 val] ...)
231 | (vector 'name id1 ... id2 ...))))
232 | (define predicate
233 | (lambda (obj)
234 | (and (vector? obj)
235 | (= (vector-length obj) count)
236 | (eq? (vector-ref obj 0) 'name))))
237 | (define access
238 | (lambda (obj)
239 | (vector-ref obj index)))
240 | ...
241 | (define assign
242 | (lambda (obj newval)
243 | (vector-set! obj index newval)))
244 | )))))))))))
245 | ```
246 | 16.3节 结构体 (Structures)
247 | 本节介绍一种机制,类似于[7.15节](../CSUG/07.OperationsOnObjects.html#7.15)的 `record-defining`机制,该机制 允许使用固定的命名字段集创建数据结构。与记录类型不同,结构类型不是唯一类型,而是实现为向量。具体而言,将结构实现为向量,其长度比字段数大一倍,并且其第一个元素包含该结构的符号名称。
248 |
249 | 将结构表示为矢量可以在某种程度上简化结构的读取和打印以及结构定义工具的扩展。但是,它确实有一些缺点。一个是在不适当的情况下,结构可能会被错误地视为普通向量。当在程序中处理结构和向量时,在检查更通用的向量类型之前,例如在一系列 `cond`子句中,必须注意寻找更具体的结构类型。一个类似的缺点是,结构实例容易被有意或无意地“伪造”。也不可能控制如何打印和读取结构。
250 |
251 | 通过 `define-structure`创建结构。每个结构定义都定义一个构造函数过程,一个类型谓词,每个字段的访问过程以及每个字段的赋值过程。 `define-structure`允许程序员控制哪些字段是所生成的构造函数过程的参数,以及哪些字段由构造函数过程显式初始化。
252 |
253 | `define-structure`很简单,但对于大多数应用程序来说足够强大,并且可以很容易地扩展以处理许多应用程序,但这些结构还不够。本节末尾给出的定义结构定义可以作为更复杂变体的起点。
254 |
255 | 语法:`(define-structure (name id1 ...) ((id2 expr) ...))`
256 |
返回:未指定的
257 |
所在库:(chezscheme)
258 |
259 | `define-structure`形式是一种定义,可以出现在任何地方,并且只能出现在其他定义出现的地方。
260 |
261 | `define-structure`定义一个新的数据结构 name,并创建一组用于创建和操作该结构实例的过程。标识符 `id1 ...`和 `id2 ...` 命名数据结构的字段。
262 |
263 | 以下过程由 `define-structure`定义:
264 | * 一个名为 `make-name`的构造函数过程,
265 | * 一个名为 `name?`的类型谓词,
266 | * 一个访问过程,其名称是每个字段名 `id1 ...`和 `id2 ...`的 `name-field`,以及
267 | * 一个用于每个字段名 `id1 ...`和 `id2 ...`, 名为 `set-name-field!`的赋值过程。
268 |
269 | 由标识符 `id1 ...`命名的字段是由构造函数过程的参数初始化的。由标识符 `id2 ...`命名的字段被显式初始化为表达式 `expr ...`的值。每个表达式在标识符 `id1 ...`的范围内求值(绑定到相应的字段值)和任何标识符 `id2 ...`(绑定到相应的字段值)出现在它前面(就像在 `let*`中一样)。
270 |
271 | 为了说明这一点,构造函数的行为可以定义为
272 | ```
273 | (define make-name
274 | (lambda (id1 ...)
275 | (let* ([id2 expr] ...)
276 | body ) ) )
277 | ```
278 | 其中 body根据标识符 `id1 ...`和 `id2 ...`的值构建结构。
279 |
280 | 如果不需要除构造函数过程的参数初始化的字段以外的其他字段,则可以省略第二个子表达式 `((id2 expr) ...)`。
281 |
282 | 下面的简单示例演示了如果不存在对,如何在 Scheme中定义它们。这两个字段都由构造函数过程的参数初始化。
283 | ```
284 | (define-structure (pare car cdr))
285 | (define p (make-pare 'a 'b))
286 |
287 | (pare? p) => #t
288 | (pair? p) => #f
289 | (pare? '(a . b)) => #f
290 |
291 | (pare-car p) => a
292 | (pare-cdr p) => b
293 |
294 | (set-pare-cdr! p (make-pare 'b 'c))
295 |
296 | (pare-car (pare-cdr p)) => b
297 | (pare-cdr (pare-cdr p)) => c
298 | ```
299 | 下面的示例定义了一个方便的字符串数据结构,称为`stretch-string`,可以根据需要进行增长。此示例使用显式初始化为依赖于构造函数参数字段值的值的字段。
300 | ```
301 | (define-structure (stretch-string length fill)
302 | ([string (make-string length fill)]))
303 |
304 | (define stretch-string-ref
305 | (lambda (s i)
306 | (let ([n (stretch-string-length s)])
307 | (when (>= i n) (stretch-stretch-string! s (+ i 1) n))
308 | (string-ref (stretch-string-string s) i))))
309 |
310 | (define stretch-string-set!
311 | (lambda (s i c)
312 | (let ([n (stretch-string-length s)])
313 | (when (>= i n) (stretch-stretch-string! s (+ i 1) n))
314 | (string-set! (stretch-string-string s) i c))))
315 |
316 | (define stretch-string-fill!
317 | (lambda (s c)
318 | (string-fill! (stretch-string-string s) c)
319 | (set-stretch-string-fill! s c)))
320 |
321 | (define stretch-stretch-string!
322 | (lambda (s i n)
323 | (set-stretch-string-length! s i)
324 | (let ([str (stretch-string-string s)]
325 | [fill (stretch-string-fill s)])
326 | (let ([xtra (make-string (- i n) fill)])
327 | (set-stretch-string-string! s
328 | (string-append str xtra))))))
329 | ```
330 | 通常,大多数自动定义的过程仅用于定义更专门的过程,在这种情况下,这些过程为 `stretch-string-ref`和 `stretch-string-set!`。`stretch-string-length`和 `stretch-string-string`是唯一可能在使用拉伸字符串的代码中直接调用的自动定义的过程。
331 | ```
332 | (define ss (make-stretch-string 2 #\X))
333 |
334 | (stretch-string-string ss) => "XX"
335 | (stretch-string-ref ss 3) => #\X
336 | (stretch-string-length ss) => 4
337 | (stretch-string-string ss) => "XXXX"
338 |
339 | (stretch-string-fill! ss #\@)
340 | (stretch-string-string ss) => "@@@@"
341 | (stretch-string-ref ss 5) => #\@
342 | (stretch-string-string ss) => "@@@@@@"
343 |
344 | (stretch-string-set! ss 7 #\=)
345 | (stretch-string-length ss) => 8
346 | (stretch-string-string ss) => "@@@@@@@="
347 | ```
348 | Scheme编程语言第4版第8.4节定义了 define-structure的简化变体,作为使用语法 case的示例。下面给出的定义实现了完整的版本。
349 |
350 | `define-structure`扩展为一系列由结构名称和字段名称生成的名称的定义。生成的标识符使用 `datum->syntax`使标识符在`define-structure`形式出现的地方可见。由于 `define-structure`表单扩展为包含定义的 begin,因此它本身就是一个定义,可以在定义有效的地方使用。
351 | ```
352 | (define-syntax define-structure
353 | (lambda (x)
354 | (define gen-id
355 | (lambda (template-id . args)
356 | (datum->syntax template-id
357 | (string->symbol
358 | (apply string-append
359 | (map (lambda (x)
360 | (if (string? x)
361 | x
362 | (symbol->string
363 | (syntax->datum x))))
364 | args))))))
365 | (syntax-case x ()
366 | ((_ (name field1 ...))
367 | (andmap identifier? #'(name field1 ...))
368 | #'(define-structure (name field1 ...) ()))
369 | ((_ (name field1 ...) ((field2 init) ...))
370 | (andmap identifier? #'(name field1 ... field2 ...))
371 | (with-syntax
372 | ((constructor (gen-id #'name "make-" #'name))
373 | (predicate (gen-id #'name #'name "?"))
374 | ((access ...)
375 | (map (lambda (x) (gen-id x #'name "-" x))
376 | #'(field1 ... field2 ...)))
377 | ((assign ...)
378 | (map (lambda (x) (gen-id x "set-" #'name "-" x "!"))
379 | #'(field1 ... field2 ...)))
380 | (structure-length
381 | (+ (length #'(field1 ... field2 ...)) 1))
382 | ((index ...)
383 | (let f ([i 1] [ids #'(field1 ... field2 ...)])
384 | (if (null? ids)
385 | '()
386 | (cons i (f (+ i 1) (cdr ids)))))))
387 | #'(begin
388 | (define constructor
389 | (lambda (field1 ...)
390 | (let* ([field2 init] ...)
391 | (vector 'name field1 ... field2 ...))))
392 | (define predicate
393 | (lambda (x)
394 | (and (vector? x)
395 | (#3%fx= (vector-length x) structure-length)
396 | (eq? (vector-ref x 0) 'name))))
397 | (define access (lambda (x) (vector-ref x index)))
398 | ...
399 | (define assign
400 | (lambda (x update) (vector-set! x index update)))
401 | ...))))))
402 | ```
403 | 16.4节 兼容性文件 (Compatibility File)
404 | Chez Scheme的当前版本与兼容性文件一起发布,该兼容性文件包含 Chez Scheme早期版本支持的各种语法形式和过程的定义,此后不再提供支持。此文件 compat.ss通常安装在 Chez Scheme安装目录的 library子目录中。
405 |
406 | 在某些情况下,由于不经常使用 compat.ss中的表格和过程,因此很容易直接将它们写在 Scheme中。 在其他情况下,系统的改进使表格和程序过时了。 在这种情况下,应编写新代码以使用较新的功能,并且应尽可能重写较旧的代码以使用较新的功能。
407 |
408 | ---
409 | [Chez Scheme 第 9 版用户手册](../CSUG)
410 |
思科系统有限公司版权所有 © 2019
411 |
按 [Apache License Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) 授权([完整的版权公告](http://cisco.github.io/ChezScheme/csug9.5/canned/copyright.html))
412 |
这是 2019年 3月的修订版本, 对应于 Chez Scheme 9.5.2
413 |
[关于本书](http://cisco.github.io/ChezScheme/csug9.5/canned/about.html)
414 |
415 |
--------------------------------------------------------------------------------
/COPYING:
--------------------------------------------------------------------------------
1 | For Chinese translation
2 |
3 | Commercial use is strictly prohibited
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Chez Scheme 用户手册
2 |
3 | 电子书: [GitHub镜像](https://guenchi.github.io/CSUG) [GitLab镜像](https://guenchi.gitlab.io/CSUG)
4 |
5 | 项目: [GitHub(主要)](https://github.com/guenchi/CSUG) [GitLab(仅镜像)](https://gitlab.com/guenchi/CSUG)
6 |
7 | 翻译活动经费由@guenchi出资赞助: 1000字/100元
8 |
9 | ### [翻译任务清单](https://github.com/guenchi/CSUG/blob/master/work.md)
10 |
11 | ```
12 | 原著:Kent R. Dybvig
13 | 编辑:guenchi
14 | ```
15 |
16 | ### [00. 术语表](00.Terminology.md)
17 |
18 |
19 | ### [01. 简介](01.Introduction.md)
20 |
21 | ```
22 | 译者:flowingfirefly (完成)
23 | ```
24 |
25 | ### [02. 使用Chez Scheme](02.UsingChezScheme.md)
26 |
27 | ```
28 | 译者:flowingfirefly(未完成)
29 | ```
30 |
31 | ### [03. Debugging](03.Debugging.md)
32 |
33 | ```
34 | 译者:shenxs(未完成)
35 | ```
36 |
37 | ### [04. 外部接口](04.ForeignInterface.md)
38 |
39 | ### [05. 绑定](05.BindingForms.md)
40 |
41 | ### [06. 控制结构](06.ControlStructures.md)
42 |
43 | ### [07. 对象操作](07.OperationsOnObjects.md)
44 |
45 | ```
46 | 译者:xashes(完成)
47 | ```
48 |
49 | ### [08. 数字操作](08.NumericOperations.md)
50 |
51 | ```
52 | 译者:xashes(完成)
53 | ```
54 |
55 | ### [09. 输入输出操作](09.InputOutputOperations.md)
56 |
57 | ```
58 | 译者:xashes(完成)
59 | ```
60 |
61 | ### [10. 库和顶层程序](10.LibrariesAndTop-levelPrograms.md)
62 |
63 | ```
64 | 译者:DeathKing(未完成)
65 | ```
66 |
67 | ### [11. 语法扩展和模块](11.SyntacticExtensionAndModules.md)
68 |
69 | ```
70 | 译者:DeathKing(完成)
71 | ```
72 |
73 | ### [12. 系统操作](12.SystemOperations.md)
74 |
75 | ### [13. 存储系统](13.StorageManagement.md)
76 |
77 | ### [14. 扩展编辑器](14.ExpressionEditor.md)
78 |
79 | ### [15. 线程系统](15.ThreadSystem.md)
80 |
81 | ### [16. 兼容功能](16.CompatibilityFeatures.md)
82 | ```
83 | 译者:Faiz(认领)
84 | ```
85 |
86 |
87 | ### [关于此项目](README.md)
88 |
89 |
90 | ### 授权
91 |
92 | [Chez Scheme 第 9 版用户手册](http://cisco.github.io/ChezScheme/csug9.5/index.html)
93 |
94 | 思科系统公司版权所有 © 2018
95 |
96 | 按 [ Apache License Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) 授权
97 | ([完整的版权公告](http://cisco.github.io/ChezScheme/csug9.5/canned/copyright.html))
98 |
99 | 为 Chez Scheme 9.5.1 版本修改于 2018 年十月
100 |
101 | [关于本书](http://cisco.github.io/ChezScheme/csug9.5/canned/about.html)
102 |
103 | ### 译文授权
104 |
105 | ```
106 | 可用于非商业用途的自由存储,修改与传播,但必须包含版权文件与译者姓名。
107 | ```
108 |
--------------------------------------------------------------------------------
/SUMMARY.md:
--------------------------------------------------------------------------------
1 | # Summary
2 |
3 | * [首页](README.md)
4 |
5 | * [00. 术语表](00.Terminology.md)
6 |
7 | * [01. 简介](01.Introduction.md)
8 |
9 | * [02. 使用Chez Scheme](02.UsingChezScheme.md)
10 |
11 | * [03. Debugging](03.Debugging.md)
12 |
13 | * [04. 外部接口](04.ForeignInterface.md)
14 |
15 | * [05. 绑定](05.BindingForms.md)
16 |
17 | * [06. 控制结构](06.ControlStructures.md)
18 |
19 | * [07. 对象操作](07.OperationsOnObjects.md)
20 |
21 | * [08. 数字操作](08.NumericOperations.md)
22 |
23 | * [09. 输入输出操作](09.InputOutputOperations.md)
24 |
25 | * [10. 库和顶层程序](10.LibrariesAndTop-levelPrograms.md)
26 |
27 | * [11. 语法扩展和模块](11.SyntacticExtensionAndModules.md)
28 |
29 | * [12. 系统操作](12.SystemOperations.md)
30 |
31 | * [13. 存储系统](13.StorageManagement.md)
32 |
33 | * [14. 扩展编辑器](14.ExpressionEditor.md)
34 |
35 | * [15. 线程系统](15.ThreadSystem.md)
36 |
37 | * [16. 兼容功能](16.CompatibilityFeatures.md)
38 |
--------------------------------------------------------------------------------
/work.md:
--------------------------------------------------------------------------------
1 | ## 翻译任务清单
2 |
3 | 源文档:https://cisco.github.io/ChezScheme/csug9.5/csug.html
4 |
5 | 注:想认领章节翻译的同学可以到Issues中提一个任务领取的Issue,提的时候最好能同时给一个预估的翻译截止时间。
6 |
7 | | 章节 | 认领人 | 完成情况 | 开始时间 |
8 | | --------------------------------------------- | ----- | ------ | ------- |
9 | | Preface ||||
10 | | Chapter 1. Introduction | flowingfirefly | 译完了 Chapter 1 | Jan 4, 2019 |
11 | | Chapter 2. Using Chez Scheme | flowingfirefly | 认领了 Chapter 2 | Jan 9, 2019 |
12 | | Chapter 3. Debugging |shenxs |初步完成||
13 | | Chapter 4. Foreign Interface ||||
14 | | Chapter 5. Binding Forms ||||
15 | | Chapter 6. Control Structures ||||
16 | | Chapter 7. Operations on Objects | xashes | 校订 | Dec 31, 2018 |
17 | | Chapter 8. Numeric Operations | xashes | 初步完成| Jan 19, 2019|
18 | | Chapter 9. Input/Output Operations | xashes | 初步完成| Jan 26, 2019|
19 | | Chapter 10. Libraries and Top-level Programs | DeathKing |||
20 | | Chapter 11. Syntactic Extension and Modules | DeathKing | 完成(遗留术语 `fluid keyword`) | Jan 6, 2019 |
21 | | Chapter 12. System Operations ||||
22 | | Chapter 13. Storage Management |dasheng523 |初步完成||
23 | | Chapter 14. Expression Editor |dasheng523 |初步完成||
24 | | Chapter 15. Thread System |待我伸伸脚|完成(未上传PR)||
25 | | [Chapter 16. Compatibility Features](16.CompatibilityFeatures.md) | [Faiz](https://github.com/faiz-lisp) | 认领了 16.兼容功能 | Jan 14, 2020 |
26 |
27 |
28 | ## 结算
29 |
30 | **字数统计按照Scrivener最新版本字数统计功能为准**
31 |
32 | ```
33 | 统计按照一个汉字算一个字,一个英文单词算一个字。
34 | ```
35 |
36 |
37 | 第一章
38 | ```
39 | 字数:2649
40 |
41 | 段落数:59
42 | 句数:160
43 | 平均段落长度:44
44 | 平均句子长度:16
45 |
46 | => 265元 (flowingfirefly认领)
47 | ```
48 |
49 | 第七章
50 |
51 | ```
52 | 字数:16428
53 |
54 | 段落数:1428
55 | 句数:2107
56 | 平均段落长度:11
57 | 平均句子长度:7
58 |
59 | => 1643元 (xashes认领)
60 | ```
61 |
62 | 第八章
63 |
64 | ```
65 | 字数:6115
66 |
67 | 段落数:668
68 | 句数:863
69 | 平均段落长度:9
70 | 平均句子长度:7
71 |
72 | => 612元 (xashes认领)
73 | ```
74 |
75 | 第十一章
76 |
77 | ```
78 | 字数:8530
79 |
80 | 段落数:747
81 | 句数:969
82 | 平均段落长度:11
83 | 平均句子长度:8
84 |
85 | => 853元 (DeathKing认领)
86 | ```
87 |
88 | 第十二章
89 |
90 | ```
91 | 字数:?
92 |
93 | 段落数:?
94 | 句数:?
95 | 平均段落长度:?
96 | 平均句子长度:?
97 |
98 | => ?元 (未认领)
99 | ```
100 |
101 | 第十四章
102 |
103 | ```
104 | 字数:?
105 |
106 | 段落数:?
107 | 句数:?
108 | 平均段落长度:?
109 | 平均句子长度:?
110 |
111 | => ?元 (未认领)
112 | ```
113 |
114 | 第十六章
115 |
116 | ```
117 | 字数:?
118 |
119 | 段落数:?
120 | 句数:?
121 | 平均段落长度:?
122 | 平均句子长度:?
123 |
124 | => ?元 (Faiz认领)
125 | ```
126 |
127 |
128 |
129 |
282 |
283 |
284 | ***本译文严禁用于商业用途***
285 |
--------------------------------------------------------------------------------
/字数统计方法示例.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guenchi/CSUG/28fcc7574ce939ed0a111927d71ce13bb0b89fd7/字数统计方法示例.png
--------------------------------------------------------------------------------