SICP の 3.5.2 Infinite Streams をサクサク読んで、Execise 3.55 まで解きます。
ストリームは、stream-cdr
で実際にアクセスしようとした分だけ評価されます(もちろんstream-map
のような内部でstream-cdr
を呼んでいる処理も同様です)。この特徴を利用すれば、無限の長さをもつシーケンスを表すことができます。
このような無限の長さをもつストリームのすべての要素を評価することはできませんが、一部分を評価して値を取得したりすることはできます。
ストリームの定義の方法には明示的に生成する方法と暗黙的に生成する方法があります。前者は (define integers (integers-starting-from 1))
のようにストリームを生成する手続きを明示的に呼び出す方法です。後者はストリームが遅延評価されるという性質を利用して、自分の最初の要素を利用して次の要素を生成していく方法です。
Without running the program, describe the elements of the stream defined by
(define s (cons-stream 1 (add-streams s s)))
手続きs
は暗黙的に定義されています。順をおってs
要素を考えると、
最初の要素は1
。
2番目の要素は(add-streams s s)
の最初の要素になります。s
の最初の要素が1
なので、(add-streams s s)
の最初の要素は2
。
3番目の要素は(add-streams s s)
の2番目の要素になります。 s
の2番目の要素が2
なので、4
。
以下同様なので、s
は最初の要素が1
でn
番目の要素が2^(n-1)
になります。
Define a procedure 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 nth element (counting from 0) is n + 1 factorial:
(define factorials (cons-stream 1 (mul-streams <??> <??>)))
mul-streams
の定義はストリームを2つ受け取り、それぞれのn番目の要素を掛け合わせたストリームを返すものなので以下のように定義できます。
(define (mul-streams s1 s2)
(stream-map * s1 s2))
factorials
は1, 2, 6, 24...
という要素をもつストリーム。問題の定義を見ると最初の要素はすでにできているので、2番目以降の要素を(mul-streams <??> <??>
で作りたいです。以下のように定義できます。
(define factorials
(cons-stream 1 (mul-streams
factorials
(stream-cdr integers))))
factorials
の要素を順をおって調べてみます。factorials
のn番目の要素が評価されたタイミングでそれぞれの値がどうなっているか表にして見ていきましょう。
最初の要素は1
。
|-------------|------------|-----------------------|------------------------------------------------|
| n番目の要素 | factorials | (stream-cdr integers) | (mul-streams factorials (stream-cdr integers)) |
|-------------|------------|-----------------------|------------------------------------------------|
| 1 | 1 | 未評価 | 未評価 |
|-------------|------------|-----------------------|------------------------------------------------|
2番目の要素は(mul-streams factorials (stream-cdr integers)
の最初の要素なので(* (stream-car factorials) (stream-car (stream-cdr integers))
です。factorials
の最初の要素が1
なので、結果は2
となります。
|-------------|------------|-----------------------|------------------------------------------------|
| n番目の要素 | factorials | (stream-cdr integers) | (mul-streams factorials (stream-cdr integers)) |
|-------------|------------|-----------------------|------------------------------------------------|
| 1 | 1 | 2 | 2 |
| 2 | 2 | 未評価 | 未評価 |
|-------------|------------|-----------------------|------------------------------------------------|
3番目の要素は(mul-streams factorials (stream-cdr integers)
の2番目の要素なので(* <factoirialsの2番目の要素> <(stream-cdr integers)の2番目の要素>
です。factorials
の2番目の要素が2
なので、結果は6
となります。
|-------------|------------|-----------------------|------------------------------------------------|
| n番目の要素 | factorials | (stream-cdr integers) | (mul-streams factorials (stream-cdr integers)) |
|-------------|------------|-----------------------|------------------------------------------------|
| 1 | 1 | 2 | 2 |
| 2 | 2 | 3 | 6 |
| 3 | 6 | 未評価 | 未評価 |
|-------------|------------|-----------------------|------------------------------------------------|
4番目の要素は(mul-streams factorials (stream-cdr integers)
の3番目の要素なので(* <factorialsの3番目の要素> <(stream-cdr integers)の3番目の要素>
です。factorials
の3番目の要素が6
なので、結果は24
となります。
|-------------|------------|-----------------------|------------------------------------------------|
| n番目の要素 | factorials | (stream-cdr integers) | (mul-streams factorials (stream-cdr integers)) |
|-------------|------------|-----------------------|------------------------------------------------|
| 1 | 1 | 2 | 2 |
| 2 | 2 | 3 | 6 |
| 3 | 6 | 4 | 24 |
| 4 | 24 | 未評価 | 未評価 |
|-------------|------------|-----------------------|------------------------------------------------|
上記の通り、(mul-streams factorials (stream-cdr integers)
のn
番目の値が(* <n-1の階乗> n)
という形で作られていくことがわかります。
Define a procedure partial-sums that takes as argument a stream S and returns the stream whose elements are
S0, S0 + S1, S0 + S1 + S2, ....
For example,(partial-sums integers)
should be the stream1, 3, 6, 10, 15, ....
3.55 も 3.54 と同じ要領解くことができます。partial-sums
の2番目以降の要素をn+1
番目の要素(nは1以上)とすると、n+1
番目の要素はn
番目の要素+引数のストリームS
のn+1
番目の要素と表すことができます。また、partial-sums
の1番目の要素はS
の1番目の要素です。
なのでpartial-sums
は以下のように定義できます。
(define (partial-sums S)
(cons-stream (stream-car S)
(add-streams (partial-sums S)
(stream-cdr S))))
上記のコードは間違ってませんが、いちいちpartial-sums
を呼び出すのでパフォーマンスが悪いです。メモ化が行われるように修正すると以下のようになります。
(define (partial-sums S)
(define p-sums
(cons-stream (stream-car S)
(add-streams p-sums
(stream-cdr S))))
p-sums)
Execise 3.56 から Execise 3.58 までを解きます。