またまた再開した時代遅れひとりFizzBuzz祭り、今回は方向性を変えて秀丸マクロなんぞ使うてみよかと思う*1。秀丸マクロは和製テキストエディタのマクロ言語の中ではそこそこメジャーではないかと思うのだが、どうなんだろう?
「テキストエディタに拡張用の組み込み言語」という組み合わせは、その昔から生き残ってきたシェアウェアのエディタでは結構定番なように思う。例えばDana、MIFES、WZ EDITOR、そして秀丸エディタ。今ではフリーウェアを含めて山ほどテキストエディタがあるが、何らかの組み込み言語で拡張可能なものや、言語というほどではないがキーボードマクロのような機能を持つものが結構あるのは、古のテキストエディタの影響という面もあるのではないだろうか?
ところで、秀丸マクロには前回のHSPとの関連性など無いに等しいのだが……何だろう、何となく似たようなにおいを感じる。これも妙な話で、HSPと秀丸マクロとでは目的もできることも文法も違う。ハッキリ言って共通項なんて特に見つからないのだが、感じるのだ。気のせいだろうか?
そんな疑問はさておき、FizzBuzzしてみた。秀丸エディタは5年以上使っているのだが、実は他人が書いたマクロを使ったことはあっても自分でマクロを書いたことはなかった。なので秀丸マクロデビューである。
そして何気に、「時代遅れひとりFizzBuzz祭り」で初の有償な処理系である。ちゃんとお金を払って使っているので、念の為。ちなみに秀丸エディタ ver.8.03を使っている。
まずは小手調べに一般的だろうスタイルのFizzBuzz。insertというのは、カーソル位置に文字列を挿入する命令だ。
#i = 1; while (#i <= 100) { if (#i % 15 == 0) { insert "FizzBuzz"; } else if (#i % 3 == 0) { insert "Fizz"; } else if (#i % 5 == 0) { insert "Buzz"; } else { insert str(#i); } insert "\n"; #i = #i + 1; }
秀丸マクロの文法はC言語系統のものだ。但しループ構文はwhileのみで、forに該当するものは無い。またインクリメント演算子のようなものも無い。なのでC言語などに慣れた身からすると幾分か違和感というかモヤモヤ感がある。
変数は宣言せずに使用できる。データ型は数値と文字列の2種類のみだ。暗黙の型変換のようなものはないので、組み込み関数strを使って数値から文字列に変換している。
変数の接頭文字はデータ型によって異なる。数値なら `#'、文字列なら `$' を付けなければならない。
#i = 1; while (#i <= 100) { if (#i % 3 == 0) { $val = "Fizz"; } else { $val = ""; } if (#i % 5 == 0) { $val = $val + "Buzz"; } if ($val == "") { $val = str(#i); } insert $val + "\n"; #i = #i + 1; }
文字列の連結は演算子 `+' でできる。ヘルプによると、文字列の中身はver.8.00より前では8000バイト程度が上限だ。8.00以降ではこの制限は無くなったが、他の変数を含めた全体で1MB程度という制限がある。ちなみに数値は32bit符号付き整数だ。
配列も使える。事前に要素数を指定する必要はない。
$msg_table[1] = "Fizz"; $msg_table[2] = "Buzz"; $msg_table[3] = "FizzBuzz"; #i = 1; while (#i <= 100) { $msg_table[0] = str(#i); insert $msg_table[(#i % 3 == 0) + (#i % 5 == 0) * 2] + "\n"; #i = #i + 1; }
要素数や配列の次元に制限はないようだ。但し先ほどの文字列の制限と同じく、変数全体で1MB程度*2までしか使えない。
処理を分割したい場合はサブルーチンを使う。
$msg_table[1][0] = "Fizz"; $msg_table[0][1] = "Buzz"; $msg_table[1][1] = "FizzBuzz"; #i = 1; while (#i <= 100) { call fizzbuzz #i; insert $$return + "\n"; #i = #i + 1; } endmacro; fizzbuzz: $msg_table[0][0] = str(##1); return $msg_table[(##1 % 3 == 0)][(##1 % 5 == 0)];
サブルーチンfizzbuzzを定義して使っている。サブルーチンのパラメータ(引数)は最大9個までという制限がある。サブルーチン内でパラメータにアクセスするには1から始まる数字を使う。このサンプルの場合は「##1」だ。今回は数値なので `##' という接頭文字だが、これが文字列の場合は `$$' を使う。
ここでは使用していないが、サブルーチンではローカル変数が使える。ローカル変数の接頭文字も `##' や `$$' だ。
サブルーチンはreturnで終了する。この時戻り値を返すこともできる。サブルーチンの呼び出し側では、呼び出した直後に##returnないし$$returnという変数経由で戻り値を取得する。
ヘルプによると、サブルーチン中でサブルーチンを呼ぶネストは20階層程度が限度な模様。再帰呼び出しが可能かどうかは不明だ。
さて、ここまでのコードではinsert命令を使って結果をエディタの編集画面に書き出していたが、コマンドプロンプト上で何かしらのスクリプト言語でFizzBuzzするよりも遅い。
この理由は単純で、恐らくinsert命令で文字列を挿入する度に描画を行っている為だ。試しにFizzBuzzの結果を1つの文字列として生成して1回だけinsert命令で書き出すようにすると、一瞬のうちに結果が表示される。
$msg_table[1][0] = "Fizz\n"; $msg_table[0][1] = "Buzz\n"; $msg_table[1][1] = "FizzBuzz\n"; $retval = ""; #i = 1; while (#i <= 100) { $msg_table[0][0] = str(#i) + "\n"; $retval = $retval + $msg_table[!(#i % 3)][!(#i % 5)]; #i = #i + 1; } insert $retval;
但しこの方法は毎回使える訳ではないし、場合によっては文字列サイズの上限に引っかかってしまう可能性もある。
この場合は素直に画面書き換えの禁止/許可をする命令を使うべきだろう。
$msg_table[1][0] = "Fizz"; $msg_table[0][1] = "Buzz"; $msg_table[1][1] = "FizzBuzz"; disabledraw; #i = 1; while (#i <= 100) { $msg_table[0][0] = str(#i); insert $msg_table[(#i % 3 == 0)][(#i % 5 == 0)] + "\n"; #i = #i + 1; } enabledraw;
FizzBuzzの結果を書き込む前にdisabledrawで書き換えを禁止し、後でenabledrawで書き換えを許可している。この方法でも結果が一瞬で表示される。
単純にプログラミング言語として見た場合、秀丸マクロの評価は微妙なものになる。汎用な言語と比べると色々と制限があるし、機能も少ない。ツール拡張用の簡易なDSLとしてはそれらの制限はあって当然なのだが、その一方DSLとしては中途半端な文法だと感じる部分もある。
しかしよく考えれば秀丸エディタは16bitマシンの頃からあるソフトで、秀丸マクロ自体もその頃から組み込まれていた機能だ。現在よりも貧弱なハードウェア・OS環境で、しかも秀丸エディタ本体が動いている状態で作業の妨げとならないような速さ・軽さでマクロを実行できなくてはならなかったはずだ。更に言語設計や処理系の実装に関するリソースも現在ほど豊富ではなかっただろうし、それらのリソースへのアクセスも今より手間が掛かったはずだ。
これらの想像がどこまで合っているかは分からないのだが、ある程度合っていると仮定すると、秀丸マクロの出来は決して悪いものではなく、妥当なものだと思う*3。
ただ現在のようにハードウェアリソースが潤沢になり、より高水準なプログラミング言語が普及し、且つスクリプト言語の処理系の高速化に関する研究が進んでくると、秀丸マクロの文法や機能制限などに対して不満を感じるのも確かだ。何ともわがままな話だが。
まあ過去のマクロ資産との互換性の問題もある訳で、あまり大きな変化は起きないだろうと思う。