Part 6.変数の定義域(6)

ぷろぐる民!!



do {

do_something();
// ループ脱出条件にかかわる何らかの演算
// (ただしこのループ脱出後に、ここでの演算結果を用いることは
// ないものとする。)

} while (!success);

言うまでもなく、これは後判定である。これを前判定に書き換えることは難しい。do_something()を行なう前に、次に行なうdo_something()によって、ループ脱出条件を満たすかを判定する必要がある。


たとえば、do_something()が 「++i;」 success式が「i>=10」だとしよう。これを前判定に書き換えるためには、『次の「++i」によって「i>=10」を満たすかどうか』という条件を書かなければならない。これは言うまでもなく「i>=9」と書けば良い。よって、この後判定ループは、前判定ループに容易に書き換えることが出来る。(そして、foreachのような構文を前判定で実装できるなら、delimiterが不要というメリットを享受することが出来る。これは前回までの話だ)


ところが、一般的にこの手の変換は難しい。do_something()が「i+=a」,success式が「i>=100」だとしよう。次の「i+=a」で「i>=100」を満たすかどうかという条件は、「i+a>=100」となる。iに実際にaを加算してみないといけない。さきほどの例だと、「i+1>=10」となって、これを「i>=10-1」と書き換え、定数の畳み込みを行ない「i>=9」を導出している。すなわち、+1の逆変換である-1が定数10に対して容易に作用できるからこそ、このような最適化が実現できている。


こういう例を考えてみよう。do_something()が「i=f()」,success式が「i>=10」だとしよう。次の「i=f()」によって「i>=10」を満たすかどうかを判定するためには、「f()>=10」を判定しなければならない。このためには関数fを評価(実行)せざるを得ない。前判定に書き換えたところで、fの評価からは逃れられない。fに副作用(lisp的な意味の)がある場合、fを評価したことによって何らかの変数の値が変わってしまうかも知れない。これがまずい場合、前判定に書き換えると余計なコストが増加することがある。


以上のことから、foreachループを(余計なコストなしに)前判定に書き換えられたのは、いくつもの幸運が重なった結果だと言うことが出来る。