# 3.5.2无限流

> 译者： [https://sicp.comp.nus.edu.sg/chapters/68](https://sicp.comp.nus.edu.sg/chapters/68)

我们已经看到了如何支持将流作为完整实体进行操作的错觉，尽管实际上，我们仅计算需要访问的流的数量。 即使序列很长，我们也可以利用此技术将序列有效地表示为流。 更惊人的是，我们可以使用流来表示无限长的序列。 例如，考虑对正整数流的以下定义：

```js
function integers_starting_from(n) {
    return pair(n,
                () => integers_starting_from(n + 1)
               );
}
```

这是有道理的，因为`integers`将是一对`head`为1且其`tail`是产生以2开头的整数的对。这是一个无限长的流，但是在任何给定时间内，我们只能检查一个 它的有限部分。 因此，我们的程序将永远不会知道不存在整个无限流。

使用`integers`，我们可以定义其他无限流，例如不能被7整除的整数流：

```js
function is_divisible(x, y) {
    return x % y === 0;
}

const no_sevens =
    stream_filter(x => ! is_divisible(x, 7),
                  integers);
```

然后，仅通过访问此流的元素，就可以找到不能被7整除的整数：

```js
stream_ref(no_sevens, 100);
```

与`integers`相似，我们可以定义斐波纳契数的无限流：

```js
function fibgen(a, b) {
    return pair(a, () => fibgen(b, a + b));
}

const fibs = fibgen(0, 1);
```

函数`fibs`是一对，其`head`为0，并且其`tail`是对`fibgen(1, 1)`求值的保证。 当我们评估此延迟的`fibgen(1, 1)`时，将产生一对`head`为1且其`tail`可以评估`fibgen(1, 2)`的对，依此类推。

为了查看更令人兴奋的无限流，我们可以使用称为Eratosthenes 的_筛网的方法，概括`no_sevens`示例以构造无限质数流。 [[1]](68#footnote-1) 我们从以2开头的整数开始，这是第一个素数。 要获得其余的质数，我们首先从其余整数中滤除2的倍数。 这样就留下了从3开始的流，这是下一个素数。 现在，我们从该流的其余部分中过滤3的倍数。 这样就留下了从5开始的流，它是下一个素数，依此类推。 换句话说，我们通过以下筛选过程构造素数：要筛选流`S`，请形成一个流，该流的第一个元素是`S`的第一个元素，而其余元素则通过过滤所有倍数来获得 从`S`其余部分中选择`S`的第一个元素，然后筛分结果。 这个过程很容易用流操作来描述：_

```js
function sieve(stream) {
    return pair(head(stream),
                () => sieve(stream_filter(
                                x => !is_divisible(x,
                                          head(stream)),
			        stream_tail(stream)
			       )
                           )
	       );
}
```

现在，要查找特定的素数，我们只需要请求它：

```js
stream_ref(primes, 50);
```

设想由`sieve`设置的信号处理系统很有趣，如图&lt;quote&gt;亨德森图&lt;/quote&gt;所示。 [3.31](68#fig_3.31)和&lt;/quote&gt;中，该&lt;quote&gt;和&lt;/quote&gt;将流的第一个元素与流的其余部分分开。 第一个元素用于构造除数过滤器，其余部分将通过该过滤器，然后将过滤器的输出馈送到另一个筛箱。 然后将原始的第一个元素`pair`编辑到内部筛网的输出上以形成输出流。 因此，不仅流是无限的，而且信号处理器也是无限的，因为筛子中包含筛子。

<figure>![](img/0893416d0f98674f9aafe54bcdcd8cf5.jpg)**[图3.31](68#fig_3.31)** 主筛被视为信号处理系统。</figure>

## 隐式定义流

上面的`integers`和`fibs`流是通过指定生成函数的&lt;quote&gt;来定义的，这些函数显式地逐一计算流元素。 指定流的另一种方法是利用延迟评估来隐式定义流。 例如，以下表达式将流`ones`定义为无限的流：&lt;/quote&gt;

```js
const ones = pair(1, () => ones);
```

这非常类似于递归函数的定义：`ones`是一对`head`为1且其`tail`是对`ones`求值的对。 评估`tail`会再次给我们1和对`ones`进行评估的承诺，依此类推。

通过使用诸如`add_streams`之类的操作来操纵流，我们可以做更多有趣的事情，它产生两个给定流的元素和： [[3]](68#footnote-3)

```js
function add_streams(s1, s2) {
    return stream_combine((x1, x2) => x1 + x2, s1, s2);
}
```

现在我们可以如下定义整数：

```js
const integers = pair(1, () => add_streams(ones, integers));
```

这将`integers`定义为第一个元素为1且其余为`ones`和`integers`之和的流。 因此，`integers`的第二个元素是1加上`integers`的第一个元素，即2； `integers`的第三个元素是1加上`integers`的第二个元素，即3； 等等。 之所以可以使用此定义，是因为在任何时候都已经生成了足够的`integers`流，因此我们可以将其反馈到定义中以产生下一个整数。

我们可以用相同的样式定义斐波那契数：

```js
const fibs = pair(0,
                  () => pair(1,
                             () => add_streams(fibs,
                                               stream_tail(fibs))
                            )
                 );
```

此定义表示`fibs`是以0和1开头的流，因此可以通过向其自身添加`fibs`并将其移位一位来生成该流的其余部分： &lt;split&gt;![](img/0823baf0eff71c181490f1c83658fe11.jpg)&lt;/split&gt;

函数`scale_stream`在公式化此类流定义中也很有用。 这会将流中的每个项目乘以给定的常数：

```js
function scale_stream(stream, factor) {
    return stream_map(x => x * factor,
                      stream);
}
```

例如，

```js
const double = pair(1, () => scale_stream(double, 2));
```

produces the stream of powers of 2: ![1%2C%202%2C%204%2C%208%2C%2016%2C%2032%2C](img/d0c732005f01404c573c21ea2774f855.jpg) ….

质数流的替代定义可以通过以整数开头并通过测试素数来对其进行过滤来给出。 我们需要第一个素数2，才能开始：

```js
const primes = pair(2,
                    () => stream_filter(
                              is_prime, 
			      integers_starting_from(3))
		   );
```

这个定义看起来并不那么简单，因为我们将通过检查![n](img/493731e423d5db62086d0b8705dda0c8.jpg)是否被小于或等于![%5Csqrt%7Bn%7D](img/7e12cbd44e41888d258c10c614edd9f4.jpg)的质数（而不是整数）整除来测试数字![n](img/493731e423d5db62086d0b8705dda0c8.jpg)是否为质数：

```js
function is_prime(n) {
    function iter(ps) {
        return square(head(ps)) > n
               ? true
               : is_divisible(n, head(ps))
                 ? false
                 : iter(stream_tail(ps));
    }
    return iter(primes);
}
```

这是一个递归定义，因为`primes`是根据`is_prime`谓词定义的，该谓词本身使用`primes`流。 该函数起作用的原因是，在任何时候，都已生成足够的`primes`流以测试我们接下来需要检查的数字的素性。 也就是说，对于我们测试的素数的每个![n](img/493731e423d5db62086d0b8705dda0c8.jpg)，![n](img/493731e423d5db62086d0b8705dda0c8.jpg)都不是素数（在这种情况下，已经生成了一个素数除以质数），或者![n](img/493731e423d5db62086d0b8705dda0c8.jpg)是素数（在这种情况下，已经产生了素数） （即小于![n](img/493731e423d5db62086d0b8705dda0c8.jpg)的素数，即大于![%5Csqrt%7Bn%7D](img/7e12cbd44e41888d258c10c614edd9f4.jpg)的素数）。 [ [4]](68#footnote-4)

<exercise>Without running the program, describe the elements of the stream defined by

```js
const s = pair(1, () => add_streams(s, s));
```

<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#no_solution_68_1_div">Add solution</button>There is currently no solution available for this exercise. This textbook adaptation is a community effort. Do consider contributing by providing a solution for this exercise, using a Pull Request in [Github](https://github.com/source-academy/sicp).</exercise><exercise>Define a function `mul_streams`, analogous to `add_streams`, that produces the elementwise product of its two input streams. Use this together with the stream of `integers` to complete the following definition of the stream whose ![n](img/493731e423d5db62086d0b8705dda0c8.jpg)th element (counting from 0) is ![n%2B1](img/003fdff6c68a3686101a32ddf3a4ad8b.jpg) factorial:

```js
// mul_streams to be written by students
const factorials = pair(1, () => mul_streams(???, ???));
```

<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#no_solution_68_1_div">Add solution</button>There is currently no solution available for this exercise. This textbook adaptation is a community effort. Do consider contributing by providing a solution for this exercise, using a Pull Request in [Github](https://github.com/source-academy/sicp).</exercise><exercise>Define a function `partial_sums` that takes as argument a stream ![S](img/988eb79721742b0fc7fee7147ab57d0c.jpg) and returns the stream whose elements are ![S_0%2C%20S_0%2BS_1%2C%20S_0%2BS_1%2BS_2%2C](img/04b48f6e90cae4a11f389275d20ec3fe.jpg) …. For example, `partial_sums(integers)` should be the stream ![1%2C%203%2C%206%2C%2010%2C%2015%2C](img/d32b3e931f9017aa17a16fde5b9b5bc8.jpg) ….<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#no_solution_68_1_div">Add solution</button>There is currently no solution available for this exercise. This textbook adaptation is a community effort. Do consider contributing by providing a solution for this exercise, using a Pull Request in [Github](https://github.com/source-academy/sicp).</exercise><exercise>A famous problem, first raised by R. Hamming, is to enumerate, in ascending order with no repetitions, all positive integers with no prime factors other than 2, 3, or 5\. One obvious way to do this is to simply test each integer in turn to see whether it has any factors other than 2, 3, and 5\. But this is very inefficient, since, as the integers get larger, fewer and fewer of them fit the requirement. As an alternative, let us call the required stream of numbers `S` and notice the following facts about it.

*   `S`从1开始。
*   `scale_stream(S, 2)`的元素也是`S`的元素。
*   `scale_stream(S, 3)`和`scale_stream(5, S)`也是如此。
*   这些都是`S`的元素。

Now all we have to do is combine elements from these sources. For this we define a function `merge` that combines two ordered streams into one ordered result stream, eliminating repetitions:

```js
function merge(s1, s2) {
    if (is_null(s1)) {
        return s2;
    } else if (is_null(s2)) {
        return s1;
    } else {
        const s1head = head(s1);
        const s2head = head(s2);
        if (s1head < s2head) {
            return pair(s1head,
                        () => merge(stream_tail(s1), s2)
                       );
        } else if (s1head > s2head) {
            return pair(s2head,
                        () => merge(s1, stream_tail(s2))
                       );
        } else {
            return pair(s1head,
                        () => merge(stream_tail(s1), stream_tail(s2));
        }
    }
}
```

Then the required stream may be constructed with `merge`, as follows:

```js
const S = pair(1, () => merge(??, ??));
```

Fill in the missing expressions in the places marked ?? above.<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#no_solution_68_1_div">Add solution</button>There is currently no solution available for this exercise. This textbook adaptation is a community effort. Do consider contributing by providing a solution for this exercise, using a Pull Request in [Github](https://github.com/source-academy/sicp).</exercise><exercise>How many additions are performed when we compute the ![n](img/493731e423d5db62086d0b8705dda0c8.jpg)th Fibonacci number using the definition of `fibs` based on the `add_streams` function, implemented using `pair(..., () => ...)` as described in the beginning of section <ref name="sec:delayed-lists">[3.5.1](67)</ref>? Show that the number of additions is exponentially greater than if we had implemented `add_streams` using the optimization using `pair(..., memo( () => ... ))` described in the last part of section <ref name="sec:delayed-lists">[3.5.1](67)</ref>.[[5]](68#footnote-5)<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#no_solution_68_1_div">Add solution</button>There is currently no solution available for this exercise. This textbook adaptation is a community effort. Do consider contributing by providing a solution for this exercise, using a Pull Request in [Github](https://github.com/source-academy/sicp).</exercise><exercise>Give an interpretation of the stream computed by the function :

```js
function expand(num, den, radix) {	
    return pair(quotient(num * radix, den),
                expand((num * radix) % den, den, radix));
}
```

where the function `quotient` computes integer division, in which the fractional part (remainder) is discarded. What are the successive elements produced by `expand(1, 7, 10)`![%5C%2C](img/4b19862177c4e90d5251360144517e34.jpg)? What is produced by `expand(3, 8, 10)`![%5C%2C](img/4b19862177c4e90d5251360144517e34.jpg)?<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#no_solution_68_1_div">Add solution</button>There is currently no solution available for this exercise. This textbook adaptation is a community effort. Do consider contributing by providing a solution for this exercise, using a Pull Request in [Github](https://github.com/source-academy/sicp).</exercise><exercise>In section <ref name="sec:symbolic-algebra">[2.5.3](46)</ref> we saw how to implement a polynomial arithmetic system representing polynomials as lists of terms. In a similar way, we can work with _power series_, such as ![%20e%5E%7Bx%7D%20%3D%0A%20%20%20%201%2Bx%2B%5Cfrac%7Bx%5E%7B2%7D%7D%7B2%7D%2B%5Cfrac%7Bx%5E%7B3%7D%7D%7B3%5Ccdot2%7D%2B%5Cfrac%7Bx%5E%7B4%7D%7D%7B4%5Ccdot%203%5Ccdot%202%7D%2B%5Ccdots%2C%20](img/b9b78d9cc037aa0f2f9c5ccd6c7d2dd2.jpg) ![%20%5Ccos%20x%20%3D1-%5Cfrac%7Bx%5E%7B2%7D%7D%7B2%7D%2B%5Cfrac%7Bx%5E%7B4%7D%7D%7B4%5Ccdot%203%5Ccdot%202%7D-%5Ccdots%2C%20](img/2b37f3f4ece9b6fd580729600fdd9de3.jpg) ![%20%5Csin%20x%20%3Dx-%5Cfrac%7Bx%5E%7B3%7D%7D%7B3%5Ccdot%202%7D%2B%5Cfrac%7Bx%5E%7B5%7D%7D%7B5%5Ccdot%204%5Ccdot%203%5Ccdot%202%7D-%20%5Ccdots%2C%20](img/3ee3b08eef4b73ba8106db1a6f8ada3d.jpg) represented as infinite streams. We will represent the series ![a_0%20%2B%20a_1%20x%20%2B%20a_2%20x%5E2%20%2B%20a_3%20x%5E3%20%2B%20%5Ccdots](img/8657bb361435d5fb039bd08556e859e7.jpg) as the stream whose elements are the coefficients ![a_0%2C%20a_1%2C%20a_2%2C%20a_3%2C](img/f075bf7941e64398f1fe21b395af21a7.jpg) ….

1.  ![a_0%20%2B%20a_1%20x%20%2B%20a_2%20x%5E2%20%2B%20a_3%20x%5E3%20%2B%20%5Ccdots](img/8657bb361435d5fb039bd08556e859e7.jpg)系列的积分是![%20c%20%2B%20a_0%20x%20%2B%20%5Cfrac%7B1%7D%7B2%7Da_1%20x%5E2%20%2B%20%5Cfrac%7B1%7D%7B3%7Da_2%20x%5E3%20%2B%20%5Cfrac%7B1%7D%7B4%7Da_3%0A%20%20%20%20%20%20%20%20x%5E4%20%2B%20%5Ccdots%20](img/22d7d2b59ea639426635e967962ab59d.jpg)系列，其中![c](img/32624581da7de65d68eb11d4201f9bef.jpg)是任何常数。 定义一个函数`integrate_series`，该函数将流![a_0%2C%20a_1%2C%20a_2%2C](img/c45adca9ec06a7b5242ba65c1ae276dd.jpg)…表示一个幂级数，并返回流![a_0%2C%20%5Cfrac%7B1%7D%7B2%7Da_1%2C%20%5Cfrac%7B1%7D%7B3%7Da_2%2C](img/c769ae9474206830070116beaccab153.jpg)…该序列积分的非恒定项系数的流`integrate_series`。 （由于结果没有常数项，因此它不表示幂级数；当我们使用`integrate-series`时，我们将为`pair`提供适当的常数。）
2.  函数![x%5Cmapsto%20e%5Ex](img/83a9b39ff4efcf3ef4bc65cf81847387.jpg)是其自己的导数。 这意味着![e%5Ex](img/76e71d320b84f809f52f8f4d8aac302e.jpg)和![e%5Ex](img/76e71d320b84f809f52f8f4d8aac302e.jpg)的积分是相同的级数，除了常数项![e%5E0%20%3D%201](img/f8edc6b4f26695439e5fe8caa851d1c5.jpg)之外。 因此，我们可以将![e%5Ex](img/76e71d320b84f809f52f8f4d8aac302e.jpg)的序列生成为

    ```js
    const exp_series = 
        pair(1, () =&gt; integrate_series(exp_series));
    ```

    ，从正弦的导数为余弦和 余弦的导数是正弦的负数：

    ```js
    const cosine_series = pair(1, ??);	  
    const sine_series = pair(0, ??);
    ```

<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#no_solution_68_1_div">Add solution</button>There is currently no solution available for this exercise. This textbook adaptation is a community effort. Do consider contributing by providing a solution for this exercise, using a Pull Request in [Github](https://github.com/source-academy/sicp).</exercise><exercise>With power series represented as streams of coefficients as in exercise <ref name="ex:powerseries">[3.59](68#ex_3.59)</ref>, adding series is implemented by `add-streams`. Complete the definition of the following function for multiplying series:

```js
function mul_series(s1, s2) {
    pair(??, add_streams(??, ??));
}
```

You can test your function by verifying that ![sin%5E2%20x%20%2B%20cos%5E2%20x%20%3D%201](img/0d082df5da8854d995204e6c5f7b5cf9.jpg), using the series from exercise <ref name="ex:powerseries">[3.59](68#ex_3.59)</ref>.<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#no_solution_68_1_div">Add solution</button>There is currently no solution available for this exercise. This textbook adaptation is a community effort. Do consider contributing by providing a solution for this exercise, using a Pull Request in [Github](https://github.com/source-academy/sicp).</exercise><exercise>Let ![S](img/988eb79721742b0fc7fee7147ab57d0c.jpg) be a power series (exercise <ref name="ex:powerseries">[3.59](68#ex_3.59)</ref>) whose constant term is 1\. Suppose we want to find the power series ![1%2FS](img/a7b5ddd0a71986c840629f4c74e4ca7d.jpg), that is, the series ![X](img/1284cf6bcb6c2ffc47e2dd24cd1c51b8.jpg) such that ![S%5Ccdot%20X%3D%201](img/06077f47017b4ebd7bd8363bf89e3b03.jpg). Write ![S%3D1%2BS_R](img/f1cbbf65ee9bcab63be7cb203ddb64f7.jpg) where ![S_R](img/e7994a93e18f71c533b8f149e398454a.jpg) is the part of ![S](img/988eb79721742b0fc7fee7147ab57d0c.jpg) after the constant term. Then we can solve for ![X](img/1284cf6bcb6c2ffc47e2dd24cd1c51b8.jpg) as follows: \begin{eqnarray*} S \cdot X &=& 1 \\ (1+S_R)\cdot X &=& 1 \\ X + S_R \cdot X &=& 1 \\ X &=& 1 - S_R \cdot X \end{eqnarray*} In other words, ![X](img/1284cf6bcb6c2ffc47e2dd24cd1c51b8.jpg) is the power series whose constant term is 1 and whose higher-order terms are given by the negative of ![S_R](img/e7994a93e18f71c533b8f149e398454a.jpg) times ![X](img/1284cf6bcb6c2ffc47e2dd24cd1c51b8.jpg). Use this idea to write a function `invert_unit_series` that computes ![1%2FS](img/a7b5ddd0a71986c840629f4c74e4ca7d.jpg) for a power series ![S](img/988eb79721742b0fc7fee7147ab57d0c.jpg) with constant term 1. You will need to use `mul_series` from exercise <ref name="ex:mul-series">[3.60](68#ex_3.60)</ref>.<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#no_solution_68_1_div">Add solution</button>There is currently no solution available for this exercise. This textbook adaptation is a community effort. Do consider contributing by providing a solution for this exercise, using a Pull Request in [Github](https://github.com/source-academy/sicp).</exercise><exercise>Use the results of exercises <ref name="ex:mul-series">[3.60](68#ex_3.60)</ref> and <ref name="ex:invert-unit-series">[3.61](68#ex_3.61)</ref> to define a function `div_series` that divides two power series. The function `div_series` should work for any two series, provided that the denominator series begins with a nonzero constant term. (If the denominator has a zero constant term, then `div_series` should signal an error.) Show how to use `div_series` together with the result of exercise <ref name="ex:powerseries">[3.59](68#ex_3.59)</ref> to generate the power series for tangent.<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#no_solution_68_1_div">Add solution</button>There is currently no solution available for this exercise. This textbook adaptation is a community effort. Do consider contributing by providing a solution for this exercise, using a Pull Request in [Github](https://github.com/source-academy/sicp).</exercise>

* * *

[[1]](68#footnote-link-1) Eratosthenes, a third-century <sc>b.c.</sc> Alexandrian Greek philosopher, is famous for giving the first accurate estimate of the circumference of the Earth, which he computed by observing shadows cast at noon on the day of the summer solstice. Eratosthenes's sieve method, although ancient, has formed the basis for special-purpose hardware <quote>sieves</quote> that, until the 1970s, were the most powerful tools in existence for locating large primes. Since then, however, these methods have been superseded by outgrowths of the probabilistic techniques discussed in section <ref name="sec:primality">[1.2.6](17)</ref>.

[[2]](68#footnote-link-2) We have named these figures after Peter Henderson, who was the first person to show us diagrams of this sort as a way of thinking about stream processing. Each solid line represents a stream of values being transmitted. The dashed line from the `head` to the `pair` and the `filter` indicates that this is a single value rather than a stream.

[[3]](68#footnote-link-3) <split>This uses the function `stream_merge` from exercise <ref name="ex:combine-streams">[3.50](67#ex_3.50)</ref>.</split>

[[4]](68#footnote-link-4) This last point is very subtle and relies on the fact that ![p_%7Bn%2B1%7D%20%5Cleq%20p_%7Bn%7D%5E2](img/9063e730e28f0d65b30857f78f89b58b.jpg). (Here, ![p_%7Bk%7D](img/98944fe7ae80511213f713d72eb703af.jpg) denotes the ![k](img/a1c2f8d5b1226e67bdb44b12a6ddf18b.jpg)th prime.) Estimates such as these are very difficult to establish. The ancient proof by Euclid that there are an infinite number of primes shows that ![p_%7Bn%2B1%7D%5Cleq%20p_%7B1%7D%20p_%7B2%7D%5C%2C%0A%20%20%20%20%5Ccdots%5C%2C%5C%2C%20p_%7Bn%7D%20%2B1](img/156c3f201aba6614eb51662e1d5b12f0.jpg), and no substantially better result was proved until 1851, when the Russian mathematician P. L. Chebyshev established that ![p_%7Bn%2B1%7D%5Cleq%202p_%7Bn%7D](img/4d6a1f99dcfdd5ae0aa31010724c9198.jpg) for all ![n](img/493731e423d5db62086d0b8705dda0c8.jpg). This result, originally conjectured in 1845, is known as _Bertrand's hypothesis_. A proof can be found in section 22.3 of <citation>Hardy and Wright 1960</citation>.

[[5]](68#footnote-link-5) This exercise shows how call-by-need is closely related to ordinary memoization as described in exercise <ref name="ex:memoization">[3.27](60#ex_3.27)</ref>. In that exercise, we used assignment to explicitly construct a local table. Our call-by-need stream optimization effectively constructs such a table automatically, storing values in the previously forced parts of the stream.

