読者です 読者をやめる 読者になる 読者になる

レガシーコード生産ガイド

私に教えられることなら

3imp 4.6メモ

Scheme

前回に続き4.6まで読み終わったのでメモです。

4.6 TAILCALL

末尾呼び出しの最適化を行います。

文章だけだとちょっと分かり辛かったのがShiftするタイミングでした。

例えば次のコードがあるとして

(lambda [a b] (f a 123))

(f a 123)を評価する直前のスタックは、

lambdaの引数b
lambdaの引数a
その後に実行するコード(戻り先)
呼び出し元のフレーム
呼び出し元のクロージャ

となっています。

タイミングの話ですが、(f a 123)の引数とfを評価する際にはlambdaの引数a,bは残ってないといけません。bは使われていませんし、aもfと123を評価する際は必要ありませんが、それらにいちいち対処しても速くはならないかと思います。(想像です)

引数とfを評価した時点でAレジスタとスタックは次のようになります。

Aレジスタ: fが指している関数

aの値
123
lambdaの引数b
lambdaの引数a
その後に実行するコード(戻り先)
呼び出し元のフレーム
呼び出し元のクロージャ

Shiftを実行するタイミングはここ、applyの前です。引数と関数の評価が全て終われば、包んでいたlambdaの引数はもう必要ありません。

Shiftによってスタックは次のように変化します。

aの値
123
その後に実行するコード(戻り先)
呼び出し元のフレーム
呼び出し元のクロージャ

REPL

試すのを楽にするため、REPLに評価結果以外もいろいろ出力するようにしてみました。

コンパイル後コードを見やすく表示したり

f:id:phaendal:20160525191316p:plain

スタックの動きを表示したり、そのまま自動テストにコピペできるテストコードを出力したり

f:id:phaendal:20160525191326p:plain

これやるとだいぶん楽になりますね。

コードはGitHubリポジトリに置いています。まだ明らかなバグがいくつかあります。

今後

後は「読者の課題とする」的な最適化です。幸いにも日本語で書かれたそれらの記事が結構あるので、参考にしつつもうちょっと進めていきたいと思います。

広告を非表示にする