また一つ、『計算機プログラムの構造と解釈』から面白いネタが飛び出してきた。

計算機プログラムの構造と解釈

一見なんでもないようなschemeの例題から、GoogleのIndex生成アルゴリズムとして名高いMapReduceの概要を理解するための機会を得た。





あの例題の本質は何だったのか？ きっかけは、先日の「プロセスの抽象化（シーケンスへの作用）」というエントリーに関して、会社の先輩から興味深い指摘をいただいたことだった。エントリーの内容は、抽象化によって「木構造の要素に対して作用する手続き」を改善するという話だが、その改善前後の手続きをもう一度掲載する。 【A】改善前の実装 ( define ( sum-odd-squares tree ) ( cond (( null? tree ) 0 ) (( not ( pair? tree )) ( if ( odd? tree ) ( square tree ) 0 )) ( else ( + ( sum-odd-squares ( car tree )) ( sum-odd-squares ( cdr tree ))))))

【B】改善後の実装 ( define ( sum-odd-squares tree ) ( accumulate + 0 ( map square ( filter odd? ( enumerate-tree tree ))))) 僕は【B】への改善の本質的な目的として「可読性の向上」と、「部品の再利用性向上」を挙げた。しかし、先輩から以下の指摘を受けた。 ・【A】はそこまで可読性が低いのか？

・【B】では明らかに計算量が増加していることをどう説明するのか？ 確かにその通りだ。特に計算量の問題は明らかで（リスト走査の回数が増えている）、そのことには前の記事を書いた段階でも気づいていたものの、「可読性の確保（今や自明ではないが…）」と「部品化による再利用」を行うことがこの例題で伝えたい本質なのだと考えてスルーしてしまった。

実際どの程度計算量が増加していたのか。【A】の計算量（シーケンスに対する全走査１回）をnとした場合、【B】では約３nの計算量を要することがわかる。 n + n + n/2 + n/2 = 3n ※enumerationとfilterが全走査であるため「n」、mapとaccumulationは奇数要素のみに対する作用であるため「n/2」となる。 つまり、計算量を３倍にしてでも【B】のスタイルをとる意味はあるのか？というのが先輩の問いだった。しばらく考えたものの、僕には明確なメリットを提示することができなかった。



