読者コミュニティ|Gaucheの習得およびProjectEulerへの挑戦の記録
本の感想や質問をお気軽にコメントしてください。
Problem 2 についてです。
fib
を定義するときはまだ fib
に何も入っていないので stream-map
に渡そうとしている fib
を評価しても意味のある値は得られません。 Gauche だと未初期化を意味するエラーになるはずです。
REPL で試行錯誤していると以前の定義をうっかりと参照していることもある (そのせいで動いているように見えることもある) ので、そのあたりで何かミスがあったのではないでしょうか。
解決方法は stream-delay
を用いて評価のタイミングを遅らせることです。
(define fib
(stream-append (stream 1 1)
(stream-delay
(stream-map +
fib
(stream-cdr fib)))))
ありがとうございます。
確かに、コメントいただいた時点のソースコードでは uninitialized variable: fib
エラーが発生しますね。
お察しの通り、REPL上で試行錯誤していた際の余分な定義が悪さをしたものだとおもいます。
今後はREPLをリセットして確認してから、記事に載せようと思います。
解決方法も提案してくださり、ありがたいです。遅延ストリームを書くことが多いので、stream-delayは便利そうです。
ソースコードを修正して記事に反映させようと思います。重ね重ね、ありがとうございます。
Problem 1 について、おそらく想定されている解法は
「3 の倍数を足したもの」+「5 の倍数を足したもの」-「15 の倍数を足したもの」
なのではないかと思います。
等差数列の和は簡単な四則演算で算出できますから、実際に数列を足していくという処理は不要であり、単純に式を計算するだけで済む問題です。 「1000 未満」が百万でも百億でもほとんど差がない計算量で処理できます。
結果は間違えているわけではないので指摘すべきかどうか迷ったのですが……、数学的な知識といっても等差数列の基本的な性質をご存じないはずもないでしょうし発想できるかどうかの問題なので序盤で考え方のコツを掴んでほしいという意味で書いておくことにしました。
ところでプログラム的な部分では、数列を表すためになんでもかんでもストリームを使うのは非効率な場合があります。 ストリームはリストを遅延処理するような構造になっていてたとえ一度しか走査しないような数列であってもリストへ蓄積してしまいます。 いずれ GC に回収されますが、無駄に作って無駄に回収されるよりはジェネレータへ置き換えることを検討してみても良いかもしれません。
念のために申し添えておきますが、実行効率が良ければ良いというものでもなく (期待に対して十分であるなら) 雑に済ますことを否定するものではありません。
Problem 5 については回りくどく考えすぎてしまっているような……。
1 から 20 までの整数全てで割り切れる数字の中で最小の正の数
というのは、言い換えれば「1 から 20 までの整数全ての最小公倍数」です。 最小公倍数を計算する手続き (lcm
) は Scheme にあるのでそれを使えば短い式で計算可能です。