diff --git a/2.4.md b/2.4.md index b92ea87..fabf92c 100644 --- a/2.4.md +++ b/2.4.md @@ -358,7 +358,7 @@ True 10 ``` -字典对于每个键只能拥有最多一个值。添加新的键值对或者对某个键修改已有的值,可以使用赋值运算符来完成。 +字典的每个键最多只能拥有一个值。添加新的键值对或者修改某个键的已有值,可以使用赋值运算符来完成。 ```py >>> numerals['I'] = 1 @@ -386,11 +386,11 @@ True 字典也有一些限制: + 字典的键不能是可变内建类型的对象。 -+ 对于一个给定的键只能有最多一个值。 ++ 一个给定的键最多只能有一个值。 第一条限制被绑定到了 Python 中字典的底层实现上。这个实现的细节并不是这门课的主题。直觉上,键告诉了 Python 应该在内存中的哪里寻找键值对;如果键发生改变,键值对就会丢失。 -第二个限制是字典抽象的结果,它被设计来储存和获取某个键的值。如果字典中只存在最多一个这样的值,我们只能获取到某个键的一个值。 +第二个限制是字典抽象的结果,它为储存和获取某个键的值而设计。如果字典中最多只存在一个这样的值,我们只能获取到某个键的一个值。 由字典实现的一个实用方法是`get`,如果键存在的话,它返回键的值,否则返回一个默认值。`get`的参数是键和默认值。 @@ -460,31 +460,31 @@ True 可变数据允许我们模拟带有变化的系统,也允许我们构建新的抽象类型。在这个延伸的实例中,我们组合了非局部赋值、列表和字典来构建一个基于约束的系统,支持多个方向上的计算。将程序表达为约束是一种声明式编程,其中程序员声明需要求解的问题结构,但是抽象了问题解决方案如何计算的细节。 -计算机程序通常组织为单方向的计算,它在预先设定的参数上执行操作,来产生合理的输出。另一方面,我们通常希望根据数量上的关系对系统建模。列入,我们之前考虑过理想气体定律,它通过波尔兹曼常熟`k`关联了理想气体的气压`p`,体积`v`,数量`n`以及温度`t`。 +计算机程序通常组织为单方向的计算,它在预先设定的参数上执行操作,来产生合理的输出。另一方面,我们通常希望根据数量上的关系对系统建模。例如,我们之前考虑过理想气体定律,它通过波尔兹曼常数`k`关联了理想气体的气压`p`,体积`v`,数量`n`以及温度`t`。 ``` p * v = n * k * t ``` -这样一个方程并不是单方向的。给定任何四个数量,我们可以使用这个方程来计算第五个。但将这个方程翻译为某种传统的计算机语言会强迫我们选择一个数量,根据其余四个计算出来。所以计算气压的函数应该不能用于计算温度、即使通过相同的方程来完成二者的计算。 +这样一个方程并不是单方向的。给定任何四个数量,我们可以使用这个方程来计算第五个。但将这个方程翻译为某种传统的计算机语言会强迫我们选择一个数量,根据其余四个计算出来。所以计算气压的函数应该不能用于计算温度,即使二者的计算通过相同的方程完成。 -这一节中,我们从零开始设计线性计算的通用模型。我们定义了数量之间的基本约束,就像`adder(a, b, c)`会严格保证数学关系`a + b = c`。 +这一节中,我们从零开始设计线性计算的通用模型。我们定义了数量之间的基本约束,例如`adder(a, b, c)`会严格保证数学关系`a + b = c`。 -我们也定义了组合的手段,使基本约束可以被组合来表达更复杂的关系。遮掩个,我们的程序就像一种编程语言。我们通过构造网络来组合约束,其中约束由连接器连接。连接器是一种对象,它“持有”一个值,并且可能会参与一个或多个约束。 +我们也定义了组合的手段,使基本约束可以被组合来表达更复杂的关系。这样,我们的程序就像一种编程语言。我们通过构造网络来组合约束,其中约束由连接器连接。连接器是一种对象,它“持有”一个值,并且可能会参与一个或多个约束。 -例如,我们直到华氏和摄氏温度的关系是: +例如,我们知道华氏和摄氏温度的关系是: ``` 9 * c = 5 * (f - 32) ``` -这个等式是`c`和`f`之间的复杂约束。这种阅读可以想象为包含`adder`、`multiplier`和`contant`约束的网络。 +这个等式是`c`和`f`之间的复杂约束。这种约束可以看做包含`adder`、`multiplier`和`contant`约束的网络。 ![](img/constraints.png) -这张图中,我们可以看到,左边是一个带有三个终端的乘法器盒子,标记为`a`,`b`和`c`。它们将乘法器连接到网络剩余的部分:终端`a`链接到了连接器`celsius`上,它持有摄氏温度。终端`b`链接到了连接器`w`上,它也链接到持有`9`的盒子上。终端`c`,被乘法器盒子约束为`a`和`b`的乘积,链接到另一个乘法器盒子上,它的`b`链接到常数`5`上,以及它的`a`连接到了求和约束的一个式子上。 +这张图中,我们可以看到,左边是一个带有三个终端的乘法器盒子,标记为`a`,`b`和`c`。它们将乘法器连接到网络剩余的部分:终端`a`链接到了连接器`celsius`上,它持有摄氏温度。终端`b`链接到了连接器`w`上,`w`也链接到持有`9`的盒子上。终端`c`,被乘法器盒子约束为`a`和`b`的乘积,链接到另一个乘法器盒子上,它的`b`链接到常数`5`上,以及它的`a`连接到了求和约束的一项上。 -通过这个网络的计算会如下进行:当连接器被提供一个值时(被用户或被链接到它的约束器),它会唤醒所有相关的约束(除了刚刚唤醒的约束)来通知它们它得到了一个值。每个唤醒的约束之后会调查它的连接器,来看看是否有足够的信息来为连接器求出一个值。如果可以,盒子会设置这个连接器,它之后会唤醒所有相关的约束,以此类推。例如,在摄氏温度和华氏温度的转换中,`w`、`x`和`y`会被常量盒子`9`、`5`和`32`立即设置。连接器会唤醒乘法器和加法器,它们判断出没有足够用于处理的信息。如果用户(或者网络的其它部分)将`celsis`连接器设置为某个值(比如`25`),最左边的乘法器会被唤醒,之后它会将`u`设置为`25 * 9 = 225`。之后`u`会唤醒第二个乘法器,它会将`v`设置为`45`,之后`v`会唤醒加法器,它将`fahrenheit`连接器设置为`77`。 +这个网络上的计算会如下进行:当连接器被提供一个值时(被用户或被链接到它的约束器),它会唤醒所有相关的约束(除了刚刚唤醒的约束)来通知它们它得到了一个值。每个唤醒的约束之后会调查它的连接器,来看看是否有足够的信息来为连接器求出一个值。如果可以,盒子会设置这个连接器,连接器之后会唤醒所有相关的约束,以此类推。例如,在摄氏温度和华氏温度的转换中,`w`、`x`和`y`会被常量盒子`9`、`5`和`32`立即设置。连接器会唤醒乘法器和加法器,它们判断出没有足够的信息用于处理。如果用户(或者网络的其它部分)将`celsis`连接器设置为某个值(比如`25`),最左边的乘法器会被唤醒,之后它会将`u`设置为`25 * 9 = 225`。之后`u`会唤醒第二个乘法器,它会将`v`设置为`45`,之后`v`会唤醒加法器,它将`fahrenheit`连接器设置为`77`。 **使用约束系统。**为了使用约束系统来计算出上面所描述的温度计算,我们首先创建了两个具名连接器,`celsius`和`fahrenheit`,通过调用`make_connector`构造器。 @@ -508,11 +508,11 @@ p * v = n * k * t >>> make_converter(celsius, fahrenheit) ``` -我们会使用消息传递系统来协调约束和连接器。我们不会使用函数来应答消息,而是使用字典。用于分发的字典会拥有字符串类型的键,代表它接受的信息。关键这些键的值是这些消息的响应。 +我们会使用消息传递系统来协调约束和连接器。我们不会使用函数来响应消息,而是使用字典。用于分发的字典拥有字符串类型的键,代表它接受的消息。这些键关联的值是这些消息的响应。 约束是不带有局部状态的字典。它们对消息的响应是非纯函数,这些函数会改变所约束的连接器。 -连接器是一个字典,持有当前值并响应操作该值的消息。约束不会直接改变链接器的值,而是会通过发送消息来改变,于是连接器可以提醒其他约束来响应变化。这样,连接器代表了一个数值,同时封装了连接器的行为。 +连接器是一个字典,持有当前值并响应操作该值的消息。约束不会直接改变连接器的值,而是会通过发送消息来改变,于是连接器可以提醒其他约束来响应变化。这样,连接器代表了一个数值,同时封装了连接器的行为。 我们可以发送给连接器的一种消息是设置它的值。这里,我们(`'user'`)将`celsius`的值设置为`25`。 @@ -522,7 +522,7 @@ Celsius = 25 Fahrenheit = 77.0 ``` -不仅仅是`celsius`的值变成了`25`,它的值在网络上传播,于是`fahrenheit`的值也发生变化。这些变化打印了出来,因为我们在构造这两个连接器的时候命名了它们。 +不仅仅是`celsius`的值变成了`25`,它的值也在网络上传播,于是`fahrenheit`的值也发生变化。这些变化打印了出来,因为我们在构造这两个连接器的时候命名了它们。 现在我们可以试着将`fahrenheit`设置为新的值,比如`212`。 @@ -531,7 +531,7 @@ Fahrenheit = 77.0 Contradiction detected: 77.0 vs 212 ``` -连接器报告说,它感觉到了一个矛盾:它的值是`77.0`,但是有人尝试将其设置为`212`。如果我们真的想以新的值复用这个网络,我们可以让`celsius`忘掉旧的值。 +连接器报告说,它察觉到了一个矛盾:它的值是`77.0`,但是有人尝试将其设置为`212`。如果我们真的想以新的值复用这个网络,我们可以让`celsius`忘掉旧的值。 ```py >>> celsius['forget']('user') @@ -551,13 +551,13 @@ Celsius = 100.0 这个新值在网络上传播,并强迫`celsius`持有值`100`。我们已经使用了非常相似的网络,提供`fahrenheit`来计算`celsius`,以及提供`celsius`来计算`fahrenheit`。这个无方向的计算就是基于约束的网络的特征。 -**实现约束系统。**像我们看到的那样,连接器是字典,将消息名称映射函数和数据值上。我们将要实现响应下列消息的连接器: +**实现约束系统。**像我们看到的那样,连接器是字典,将消息名称映射为函数和数据值。我们将要实现响应下列消息的连接器: + `connector['set_val'](source, value)` 表示`source`请求连接器将当前值设置为该值。 + `connector['has_val']()` 返回连接器是否已经有了一个值。 + `connector['val']` 是连接器的当前值。 + `connector['forget'](source)` 告诉连接器,`source`请求它忘掉当前值。 -+ `connector['connect'](source)` 告诉链接器参与新的约束`source`。 ++ `connector['connect'](source)` 告诉连接器参与新的约束`source`。 约束也是字典,接受来自连接器的以下两种消息: @@ -600,7 +600,7 @@ Celsius = 100.0 叫做`constraint`的字典是个分发字典,也是约束对象自身。它响应两种约束接收到的消息,也在对连接器的调用中作为`source`参数传递。 -无论约束什么时候被通知,它的连接器之一拥有了值,约束的局部函数`new_value`都会被调用。这个函数首先检查是否`a`和`b`都拥有值,如果是这样,它告诉`c`将值设为函数`ab`的返回值,在`adder`中是`add`。约束,也就是`adder`对象,将自身作为`source`参数传递给连接器。如果`a`和`b`不是同时拥有值,约束会检查`a`和`c`,以此类推。 +无论约束什么时候被通知,它的连接器之一拥有了值,约束的局部函数`new_value`都会被调用。这个函数首先检查是否`a`和`b`都拥有值,如果是这样,它告诉`c`将值设为函数`ab`的返回值,在`adder`中是`add`。约束,也就是`adder`对象,将自身作为`source`参数传递给连接器。如果`a`和`b`不同时拥有值,约束会检查`a`和`c`,以此类推。 如果约束被通知,连接器之一忘掉了它的值,它会请求所有连接器忘掉它们的值(只有由约束设置的值会被真正丢掉)。 @@ -672,8 +672,8 @@ Celsius = 100.0 c[message]() ``` -如果一个连接器被请求忘掉它的值,它会调用局部函数`forget_value`,这个函数首先执行检查来确保请求来自之前设置该值的同一个约束。如果是的话,连接器通知相关的约束来丢掉当前值。 +如果一个连接器被请求忘掉它的值,它会调用局部函数`forget_value`,这个函数首先执行检查,来确保请求来自之前设置该值的同一个约束。如果是的话,连接器通知相关的约束来丢掉当前值。 -对`has_val`消息的相应表示连接器是否拥有一个值。对`connect`消息的相应将`source`约束添加到约束列表中。 +对`has_val`消息的响应表示连接器是否拥有一个值。对`connect`消息的响应将`source`约束添加到约束列表中。 -我们设计的约束程序引入了许多出现在面向对象编程的概念。约束和连接器都是抽象,它们通过消息来操作。当连接器的值由消息改变时,消息不仅仅改变了它的值,还对其验证(检查来源)并传播它的效果。实际上,在这一章的后面,我们会使用相似的字符串值的字典结构和函数值来实现面向对象系统。 +我们设计的约束程序引入了许多出现在面向对象编程的概念。约束和连接器都是抽象,它们通过消息来操作。当连接器的值由消息改变时,消息不仅仅改变了它的值,还对其验证(检查来源)并传播它的影响。实际上,在这一章的后面,我们会使用相似的字符串值的字典结构和函数值来实现面向对象系统。