« FlashLite: SWF で FizzBuzz ってみた | メイン | FlashLite: バイトコードもいじってコードサイズを減らす »

FlashLite: コードサイズが小さいActionScript

FlashLiteのアプリを携帯端末で動かす場合、サイズの制約を満たすためにコードサイズを節約することが必要になる場合があります。コードサイズを節約するために、アクションスクリプトではどんなことができるのでしょう。短い変数名を使うなどはもちろん効果がありますが、例えばこんなこともできます。

ordを使う

数値を代入する代わりに、ord(文字列)を使うことができる場合があります。たとえば、
i = 100;
は、
i = mbord("d");
と書いてもiに代入される値は同じになります。(dのASCIIコードは100です。)
バイトコードで見てみると、
前者: 96 03 00 00 69 00 96 05 00 00 31 30 30 00 1d
後者: 96 03 00 00 69 00 96 03 00 00 64 00 36 1d
と、後者の方がサイズが小さくなります。mbordでなくordでも原理的には同じことができるはずですが、Flash Professional 8ではコンパイル時にord("d")を展開して100にしてくれるようで、前者とおなじコードが生成されました。 3桁以上の場合に得をして、SJISの文字コードの関係で使えない範囲があるので、100-128,160-223,253-255の96種類の数値に対して有効です。あとかなり使える場合は少ないと思いますが、同様にしてSJISの漢字1文字を5ケタの整数と対応させることもできます。この場合は2バイト節約可能です。

最初の文字を取り出す

文字列から一部分を取り出すにはsubstring命令を使いますが、最初の1文字だけを取り出すのならよりサイズを節約できる方法があります。
x = substring(s,1,1);
は、
x = chr(ord(s));
と書くことができます。バイトコードにすると、この場合は11バイト短くなります。このようなことができるのは、ordは最初の1文字だけを対象にするからです。
前者: 96 03 00 00 78 00 96 03 00 00 73 00 1c
     96 03 00 00 31 00 96 03 00 00 31 00 15 1d
後者: 96 03 00 00 78 00 96 03 00 00 73 00 1c
     32 33 1d
pushのまとめ効果を考慮に入れても8バイト短くなります。
前者: 96 06 00 00 78 00 00 73 00 1c
     96 06 00 00 31 00 00 31 00 15 1d
後者: 96 06 00 00 78 00 00 73 00 1c
     32 33 1d

実はたくさん使える短い名前の変数

マニュアルによれば、「変数名には、英数字とドル記号 ($) のみを使用できます。変数名の先頭に数字は指定できません。」とあります。しかもFlashLiteの場合変数名の大文字小文字は区別されませんから、1文字の変数はa-zと$の27種類しか使えないということになります。 しかしこれはアクションスクリプトの文法上の制限で、実はもっとたくさんの1文字変数が使えます。 ちょっと復習になりますが、以下の2つの文は同じx1という変数に値を代入しています。これは配列エミュレートのために使われますね。
x1 = 12345;
eval("x" add 1) = 12345;
数字を連結することは必ずしも必要なく、
x = 12345;
eval("x") = 12345;
上の2文は全く等価なものです。
eval("x") = 12345;
trace(eval("x"));
とすれば当然12345が出力されます。
では、以下を実行したら何が起きるでしょう。
eval("1") = 12345;
trace(eval("1"));



実はこれを実行しても12345が出力されます。1という変数に値が代入されたのです。当然1 = 12345;という書き方はできませんが、evalを使うことで空白文字でも"\n"でも変数名に使うことができます。1バイトの文字列として使える文字の数は195個ありますが、その中で大文字小文字の区別を考慮して、/, ., :は特別な意味に扱われそう(未調査)なので除外すると、全部で166個の1文字変数を使えることになります。

未定義の変数の活用

未定義の変数を参照すると空文字列が返されます。これは、数値としては0のように扱われます。 たとえば下の100から109までを出力するプログラムでは、
for(i=0;i<10;++i){
  trace(100 + i);
}
iがそれまでに使われないのが明らかであれば最初のi=0の初期化は不要ということになります。

応用編

特殊な1文字変数と未定義の変数値についての知識を応用すると、以下のようなことが書けます。
普通のスクリプト(47バイト)
b = (x==y)?"Hello":"";

同じことをする別な書き方(40バイト)
eval(1) = "Hello";
b = eval(x==y);
ただし、変数"0"は未定義であるとします。x==yは0または1になります。それをevalすることで変数"0"または変数"1"の値をbに代入しています。

注意点

FlashLiteでコードサイズを小さくするための工夫をいくつか紹介しましたが、これらを適用しても、条件によっては必ずしもコードサイズが減るわけではありません。
また、ほぼ間違いなくコードが読みにくくなってしまうと思います。

トラックバック

このエントリーのトラックバックURL:
http://labs.cybozu.co.jp/cgi-bin/mt-admin/mt-tbp.cgi/1307

コメント

> 実はたくさん使える短い名前の変数
配列(のシミュレート)の
高速化も期待できそうですね。
テンポラリな用途でないとダメかもしれませんが...

勉強になりました。

コメントを投稿