Island Life

< S式の呪縛 | 近頃のらむ太 >

2010/11/28

applyの引数リストの長さの制限

http://twitter.com/cddddr/status/8982330207117312

タイプ数削減が目的で (fold-left f acc xs) を (apply f xs) と書くとリストが長すぎる場合怒られる事があるので xargs みたいな要領の xapply を書こうかなと思ったけど面倒なので fold-left で良いやという結論に落ち着いた

処理系の制限で短く書けないというのは癪に障る。 昔はGaucheでもapplyの引数リストに長いリストを渡せなくていらついたものだった。 十分いらついたので、今はこの通り。

gosh> (apply + (make-list 10000000 1))
10000000

引数の長さの制限は依然として存在するんだけど、 それが問題になることがほぼ無くなった。

VMの引数渡しは原則スタック経由、つまりcallerが引数を順にスタックに積んで、 caller側はスタックポインタ相対で引数にアクセスするようになってる。 Gauche VMはスタックが溢れるとヒープに移すようになってるけど、 スタックフレーム単位の処理なので、ひとつのスタックフレームの大きさが VMのスタックエリアを越えるような事態は想定してない。

以前のapplyは引数リストを分解してスタックに積んでた。 なので上のような式だとスタックが足りなくなる。

けれど、長大な引数リストを取るような関数ってのは、まず確実に、 不定長引数を取るような関数なんだよね。で、不定長の残余引数は calleeからはリストに見える。 つまり以前のapplyでは、わざわざリストで渡ってきた引数を 一回ばらしてスタックに積んで、それをcallee側でまたリストに直してた。

今は、callerが不定長の引数リストをリストのままスタックに 積めるようになってて、callee側の必須引数の数に合わせて VMが必要な分だけスタックに展開するようにしてる。 + は必須引数が0なので、上の式だとリストは一切展開されない。

ただ、Schemeの規格上、残余引数リストはfreshなリストでなければならない (calleeが残余引数リストを破壊することが許されている)ので、 リストをコピーして渡さないとならない。CLだとコピーはcallee側の責任なんだけど。 このへんはまだ工夫の余地あり。

今のVMで引数の数の制限に引っかかるのは、 caller側が実際に10000個くらいの引数を列挙するか、 callee側が10000個くらいの仮引数を持っている場合。

前者はまあ、マクロ展開の結果として生じる可能性はあるけれど、 後者のケースでない限りはapply使えば回避できるんで、 実用上は問題ないかと。

今書いてて思いついたんだが、後者のケースでも処理系がコンパイル時に 一定数以上の仮引数は残余引数リストで受け取るようにしてlist-refで 参照する、とか変換かけちゃえばいいんだな。

Tags: Gauche, Lisp, Scheme, Programming

Post a comment

Name: