@@ -2802,9 +2802,9 @@ function div_complex(z1, z2) {
为了使不同的选择具体化,想象有两个程序员,Ben Bitdiddle 和 Alyssa P. Hacker,他们独立地设计复数系统的表示。Ben 选择用矩形来表示复数。有了这个选择,选择一个复数的实部和虚部就简单了,就像用给定的实部和虚部构造一个复数一样。为了找到幅度和角度,或者用给定的幅度和角度构造一个复数,他使用了三角关系
| x = r cos A | r = ![c2-fig-5011.jpg](img/c2-fig-5011.jpg) |
| y=rsinA | A = arctan( y , x ) |
| y=rsinA | A = arctan(`y`, x ) |
其将实部和虚部( x 、 y )与幅度和角度( r 、 A )相关联。因此,本的表示由以下选择器和构造器给出:
其将实部和虚部(`x`、 y )与幅度和角度( r 、 A )相关联。因此,本的表示由以下选择器和构造器给出:
```js
functionreal_part(z){returnhead(z);}
...
...
@@ -3701,7 +3701,7 @@ function mul_term_by_all_terms(t1, L) {
(y+1)x2+(y2+1)x+(y–1)·(y–2)x+(y3+7)
```
原因是当系统试图合并系数时,会通过`add`和`mul`进行调度。由于系数本身是多项式(在 y ,这些将使用`add_poly`和`mul_poly`组合。结果是一种“数据导向递归”,例如,调用`mul_poly`将导致递归调用`mul_poly`,以便乘以系数。如果系数的系数本身是多项式(可能用于表示三个变量的多项式),数据方向将确保系统将遵循另一层的递归调用,依此类推,通过数据结构规定的尽可能多的层。 [^(54)](#c2-fn-0054)
@@ -331,7 +331,7 @@ function random_gcd_test(trials, initial_x) {
##### 练习 3.5
蒙特卡洛积分是一种通过蒙特卡洛模拟的方式估算定积分的方法。考虑计算由谓词 P ( x , y )描述的空间区域的面积,对于该区域中的点( x , y )为真,对于不在该区域中的点为假。例如,包含在以(5,7)为中心的半径为 3 的圆内的区域由测试是否(x–5)²+(y–7)²3²的谓词来描述。要估计由这种谓词描述的区域的面积,首先要选择一个包含该区域的矩形。例如,对角位于(2,4)和(8,10)的矩形包含上面的圆。期望的积分是位于该区域中的矩形部分的面积。我们可以通过随机选取位于矩形中的点( x , y )来估计积分,并对每个点测试 P ( x , y )以确定该点是否位于该区域中。如果我们用许多点来尝试,那么落在该区域中的点的分数应该给出位于该区域中的矩形的比例的估计。因此,用这个分数乘以整个矩形的面积应该可以得到积分的估计值。
蒙特卡洛积分是一种通过蒙特卡洛模拟的方式估算定积分的方法。考虑计算由谓词 P (`x`, y )描述的空间区域的面积,对于该区域中的点(`x`, y )为真,对于不在该区域中的点为假。例如,包含在以(5,7)为中心的半径为 3 的圆内的区域由测试是否(x–5)²+(y–7)²3²的谓词来描述。要估计由这种谓词描述的区域的面积,首先要选择一个包含该区域的矩形。例如,对角位于(2,4)和(8,10)的矩形包含上面的圆。期望的积分是位于该区域中的矩形部分的面积。我们可以通过随机选取位于矩形中的点(`x`, y )来估计积分,并对每个点测试 P (`x`, y )以确定该点是否位于该区域中。如果我们用许多点来尝试,那么落在该区域中的点的分数应该给出位于该区域中的矩形的比例的估计。因此,用这个分数乘以整个矩形的面积应该可以得到积分的估计值。
图 3.1:显示了一个由三个框架组成的简单环境结构,标记为 I、II 和 III。在图中,A、B、C 和 D 是指向环境的指针。c 和 D 指向同一个环境。名字`z`和`x`绑定在第二帧,而`y`和`x`绑定在第一帧,环境 D 中`x`的值为 3。相对于环境`B`的`x`的值也是 3。这确定如下:我们检查序列中的第一帧(帧 III)并且没有找到`x`的绑定,所以我们前进到封闭环境 D 并且在帧 I 中找到绑定。另一方面,环境`A`中的`x`的值是 7,因为序列中的第一帧(帧 II)包含从`x`到 7 的绑定。关于环境 A,帧 II 中`x`到 7 的绑定被说成是遮蔽了帧 I 中`x`到 3 的绑定。
图 3.1:显示了一个由三个框架组成的简单环境结构,标记为 I、II 和 III。在图中,A、B、C 和 D 是指向环境的指针。`c`和 D 指向同一个环境。名字`z`和`x`绑定在第二帧,而`y`和`x`绑定在第一帧,环境 D 中`x`的值为 3。相对于环境`B`的`x`的值也是 3。这确定如下:我们检查序列中的第一帧(帧 III)并且没有找到`x`的绑定,所以我们前进到封闭环境 D 并且在帧 I 中找到绑定。另一方面,环境`A`中的`x`的值是 7,因为序列中的第一帧(帧 II)包含从`x`到 7 的绑定。关于环境 A,帧 II 中`x`到 7 的绑定被说成是遮蔽了帧 I 中`x`到 3 的绑定。
![c3-fig-0001.jpg](img/c3-fig-0001.jpg)
...
...
@@ -2118,7 +2118,7 @@ dAE = FL
这样的等式不是单向的。给定任何四个量,我们可以用它来计算第五个量。然而,将这个等式翻译成传统的计算机语言,将迫使我们从其他四个量中选择一个来计算。因此,用于计算面积`A`的函数不能用于计算挠度 d ,即使`A`和 d 的计算源自同一等式。 [^(34)](#c3-fn-0034)
在这一节中,我们概述了一种语言的设计,这种语言使我们能够根据关系本身来工作。该语言的基本元素是基本约束,它陈述了数量之间的特定关系。例如,`adder(a, b, c)`指定量 a 、 b 、`c`必须由等式 a+b=c,`multiplier(x, y, z)`表示约束 xy = z ,`constant(3.14, x)`表示`x`的值必须为 3.14。
在这一节中,我们概述了一种语言的设计,这种语言使我们能够根据关系本身来工作。该语言的基本元素是基本约束,它陈述了数量之间的特定关系。例如,`adder(a, b, c)`指定量`a`、`b`、`c`必须由等式 a+b=c,`multiplier(x, y, z)`表示约束 xy =`z`,`constant(3.14, x)`表示`x`的值必须为 3.14。
我们已经看到,处理并发线程的困难在于需要考虑不同线程中事件顺序的交错。例如,假设我们有两个线程,一个有三个有序事件( a 、 b 、 c ),另一个有三个有序事件( x 、 y 、 z )。如果两个线程并发运行,并且对它们如何交错执行没有限制,那么对于与两个线程的单独排序一致的事件,有 20 种不同的可能排序:
我们已经看到,处理并发线程的困难在于需要考虑不同线程中事件顺序的交错。例如,假设我们有两个线程,一个有三个有序事件(`a`、`b`、 c ),另一个有三个有序事件(`x`、`y`、 z )。如果两个线程并发运行,并且对它们如何交错执行没有限制,那么对于与两个线程的单独排序一致的事件,有 20 种不同的可能排序:
| ( a 、 b 、 c 、 x 、 y 、 z ) | ( a 、 x 、 b 、 y 、 c 、 z ) | ( x , a , b , c , y , z ) | ( x , a , y , z , b , c ) |
| ( a , b , x , c , y , z ) | ( a , x , b , y , z , c ) | ( x , a , b , y , c , z ) | ( x , y , a , b , c , z ) |
| ( a 、 b 、 x 、 y 、 c 、 z ) | ( a 、 x 、 y 、 b 、 c 、 z ) | ( x , a , b , y , z , c ) | ( x , y , a , b , z , c ) |
| ( a 、 b 、 x 、 y 、 z 、 c ) | ( a 、 x 、 y 、 b 、 z 、 c ) | ( x , a , y , b , c , z ) | ( x , y , a , z , b , c ) |
| ( a , x , b , c , y , z ) | ( a , x , y , z , b , c ) | ( x , a , y , b , z , c ) | ( x , y , z , a , b , c ) |
| (`a`、`b`、`c`、`x`、`y`、 z ) | (`a`、`x`、`b`、`y`、`c`、 z ) | (`x`,`a`,`b`,`c`,`y`, z ) | (`x`,`a`,`y`,`z`,`b`, c ) |
| (`a`,`b`,`x`,`c`,`y`, z ) | (`a`,`x`,`b`,`y`,`z`, c ) | (`x`,`a`,`b`,`y`,`c`, z ) | (`x`,`y`,`a`,`b`,`c`, z ) |
| (`a`、`b`、`x`、`y`、`c`、 z ) | (`a`、`x`、`y`、`b`、`c`、 z ) | (`x`,`a`,`b`,`y`,`z`, c ) | (`x`,`y`,`a`,`b`,`z`, c ) |
| (`a`、`b`、`x`、`y`、`z`、 c ) | (`a`、`x`、`y`、`b`、`z`、 c ) | (`x`,`a`,`y`,`b`,`c`, z ) | (`x`,`y`,`a`,`z`,`b`, c ) |
| (`a`,`x`,`b`,`c`,`y`, z ) | (`a`,`x`,`y`,`z`,`b`, c ) | (`x`,`a`,`y`,`b`,`z`, c ) | (`x`,`y`,`z`,`a`,`b`, c ) |
@@ -4076,7 +4076,7 @@ function integral(integrand, initial_value, dt) {
![c3-fig-5012.jpg](img/c3-fig-5012.jpg)
建模为`y`的输出流由包含环路的网络生成。这是因为 d²y/dt²的值取决于`y`和 dy / dt 的值,而这两者都是通过对 d²y/dt^(2^(的积分来确定的我们要编码的图表如图 3.35:所示。编写一个函数`solve_2nd`,该函数将常量 a 、`b`和 dt 以及初始值 y [0] 和 dy [0] 作为参数,并生成连续值 y))
建模为`y`的输出流由包含环路的网络生成。这是因为 d²y/dt²的值取决于`y`和 dy / dt 的值,而这两者都是通过对 d²y/dt^(2^(的积分来确定的我们要编码的图表如图 3.35:所示。编写一个函数`solve_2nd`,该函数将常量`a`、`b`和 dt 以及初始值 y [0] 和 dy [0] 作为参数,并生成连续值 y))
执行这种算法的机器必须记录两个数字,`a`和 b ,所以让我们假设这些数字存储在两个具有这些名字的寄存器中。所需的基本操作是测试寄存器`b`的内容是否为零,并计算寄存器`a`的内容除以寄存器`b`的内容的余数。余数运算是一个复杂的过程,但是现在假设我们有一个计算余数的原始设备。在 GCD 算法的每个周期,寄存器`a`的内容必须被寄存器`b`的内容替换,而`b`的内容必须被`a`的旧内容除以`b`的旧内容的余数替换。如果这些替换可以同时进行,这将是很方便的,但是在我们的寄存器机器模型中,我们将假设在每个步骤中只有一个寄存器可以被分配一个新值。为了完成替换,我们的机器将使用第三个“临时”寄存器,我们称之为`t`。(首先将余数放入`t`,然后将`b`的内容放入`a`,最后将`t`中存储的余数放入`b`。)
1. a. 对于 n ≥ 2,计算 Fib( n )所需的最大堆栈深度,用`n`给出一个公式。提示:在第 1.2.2 节中,我们认为这个进程使用的空间随着`n`线性增长。
2. b. 给出一个公式,用于计算 n ≥ 2 的 Fib( n )的总推送次数。您应该会发现推送的次数(与所用的时间密切相关)随着`n`成指数增长。提示:设 S ( n )为计算 Fib 时使用的推送次数( n )。你应该可以论证,有一个公式是用 S(n–1)S(n–2),和一些独立于`n`的固定“开销”常数`k`来表示 S ( n )。给出公式,并说出什么是 k 。然后证明 S ( n )可以表示为 aFib(n+1)+b 并给出`a`和`b`的值。
2. b. 给出一个公式,用于计算 n ≥ 2 的 Fib( n )的总推送次数。您应该会发现推送的次数(与所用的时间密切相关)随着`n`成指数增长。提示:设 S ( n )为计算 Fib 时使用的推送次数( n )。你应该可以论证,有一个公式是用 S(n–1)S(n–2),和一些独立于`n`的固定“开销”常数`k`来表示 S ( n )。给出公式,并说出什么是 k 。然后证明 S ( n )可以表示为 aFib(n+1)+`b`并给出`a`和`b`的值。