著者: 桑田 誠
Ruby をはじめとする動的なスクリプト言語は、C や Java のような静的なコンバイル型言語と比べると動作速度が遅いです。 しかし、それがそのままアプリケーションの動作速度を決定するわけではありません。 アプリケーションの動作速度には、実装言語の速度よりも、アルゴリズムやデータ構造といった知識の有無や、プログラミング上の工夫やアイデアのほうがずっと大きく影響を与えます1。
本稿では、C 言語で書かれた拡張モジュールより高速に動作する pure Ruby プログラムを示すことで、アプリケーションの速度には言語よりも知識やアイデアの方がずっと重要であることを示します。
具体的な題材としては、実アプリケーションで広く使われている eRuby の処理系を扱います (eRuby については後述)。 学校の教科書では、よくフィボナッチ数列の高速化などを使って「アルゴリズムの工夫が大事である」と説明していますが、実際のアプリケーションでフィボナッチ数列を使うことなど滅多にありません。 我々が知りたいのは自分のプログラムで使える知識であり、教科書の中でしか出てこないようなものを使って「工夫が大事」といわれても実感がわきません。
本稿ではそのようなことがないように、eRuby の処理系という、現場で広く使われているものを取り上げます。これにより、知識や工夫が大事であることを実感していただけたらと思います。
なお本稿は、RubyKaigi2007 における同名のセッション を文書化したものです。 発表資料 (pdf) も併せてご覧下さい。
今回の題材である eRuby について説明します。 eRuby(embedded Ruby) とは、テキストファイル中に Ruby の文や式を埋め込むための仕様です (大雑把にいえば PHP のようなものだと思ってください)。 list0-1 はそのサンプルです。「<%= … %>」の中に Ruby の式を、「<% … %>」の中に Ruby の文を埋め込みます。
list0-1: eRuby のサンプル
式や文が埋め込まれたテキストファイルは、Ruby のスクリプトへ「変換」され、そのまま Ruby スクリプトとして「実行」されます。この「変換」と「実行」を行うのが eRuby の処理系です。
現在、eRuby の主な処理系は次の 2 つです。
名前の混乱を避けるために、以降では前者を「C 実装 eruby」と呼ぶことにします。後者はそのまま「ERB」と呼びます。
C 実装 eruby と ERB では、変換結果に違いがあります。 list0-2 は C 実装 eruby による変換例です。 1 行ずつ print 文に変換しているのが特徴です。
list0-2: C 実装 eruby による変換例
list0-3 は ERB による変換例です。1行ずつ変換している点は C 実装 eruby と同じですが、print 文ではなく文字列結合を使っているのが特徴です。
list0-3: ERB による変換例
変換された Ruby スクリプトを実行すると、例えば list0-4 のような出力が得られます。 C 実装 eruby と ERB とでは、変換された Ruby スクリプトは違いますが、その実行結果は同じものになります。
list0-4: 実行されて得られる出力の例
ここで、このあとで使用するベンチマークデータについて説明します。ベンチマーク一式は以下からダウンロードできます。
ベンチマークで使用する eRuby ファイルは list0-5 のようになります。 ループ部分が 20 行あり、それを 20 回ループします。その前後に 53 行と 5 行のデータがついており、最終的に約 400 行の HTML ファイルを出力します。
list0-5: ベンチマーク用 eRuby ファイル (erubybench.rhtml)
なお本稿でのベンチマークでは、純粋に eRuby 処理系としてだけの能力を測るため、HTML エスケープ (サニタイズ) は行っていません。ご注意ください。
またベンチマークに使った環境は次の通りです。この環境で 10000 回実行した時間を計測しました。
C 実装 eruby と ERB のベンチマーク結果は table0-1 のようになりました。 これを見ると、C 実装 eruby は ERB と比べて 3 倍ほど高速ですが、高速なのは eRuby ファイルの変換部分だけであり、変換後の Ruby スクリプトを実行する部分ではそれほどの違いはないことがわかります。
table0-1: ベンチマーク結果
処理系 | 変換部分(sec) | 実行部分(sec) | 合計(sec) |
---|---|---|---|
C 実装 eruby | 1.390 | 13.958 | 15.348 |
ERB | 27.963 | 14.849 | 42.812 |
このように、 C 実装 eruby で速くなるのは変換部分だけであり、実行部分は速くはなりません。 この点は大事ですので覚えておいてください。
ここからは、実際に eRuby 処理系を pure Ruby で実装していきます。 それを少しずつ改良しながら、ベンチマークをとって速度を確認していきます。
まず最初の実装は list1-1 のようになります。 ポイントは「入力を1行ずつに分解して構文解析する」という点です。
list1-1: 最初の実装 (myeruby1.rb)
変換されたRubyスクリプトは list1-2 のようになります。 ポイントは「1行ずつの文字列結合に変換する (ERB とほぼ同じ)」という点です。
list1-2: 変換後のRubyスクリプト
ベンチマーク結果は table1-1 のようになりました。 変換部分が大変なボトルネックになっていることが分かります。
table1-1: ベンチマーク結果
処理系 | 変換部分(sec) | 実行部分(sec) | 合計(sec) |
---|---|---|---|
C 実装 eruby | 1.390 | 13.958 | 15.348 |
ERB | 27.963 | 14.849 | 42.812 |
MyEruby1 (initial implement) | 79.210 | 13.320 | 92.530 |
最初の実装は、大変遅いものとなりました。 これを少しずつ改良していきます。
まず最初の改良として、入力を1行ずつ分解して解析するのをやめ、入力全体をひとつの文字列としてそのまま解析するようにします (list2-1)。 これにより、1 行ずつに分解するコストがなくなるため、Ruby スクリプトへの変換部分が速くなります。
list2-1: 1行ずつに分解するのをやめる (myeruby2.rb)
また変換後の Ruby スクリプトも、1 行ずつに分解するのではなく、複数行をまとめて扱うようにします (list2-2)。 これにより、文字列結合のメソッド呼び出しが大幅に削減できるので、変換後の実行部分が速くなります。 今までだと、例えば 50 行の HTML があれば 50 回メソッドが呼び出されていたのが、この方法だと 1 回で済むわけですから、かなりの高速化が期待できます。
list2-2: 複数行をまとめて扱う
ベンチマーク結果は table2-1 のようになりました。 変換部分で 8 倍の高速化、実行部分で 70% 以上の高速化、トータルで 5 倍高速化されました。 C 実装 eruby にはまだ負けますが、ERB には余裕で勝つことができました。
table2-1: ベンチマーク結果
処理系 | 変換部分(sec) | 実行部分(sec) | 合計(sec) |
---|---|---|---|
C 実装 eruby | 1.390 | 13.958 | 15.348 |
ERB | 27.963 | 14.849 | 42.812 |
MyEruby1 (initial implement) | 79.210 | 13.320 | 92.530 |
MyEruby2 (no split lines) | 9.937 | 8.408 | 18.345 |
入力を 1 行ずつに分解するのは C や Perl でよく見かけますが、それにならう必要性はありません。 メモリ容量がよほど厳しい場合は別として、通常は入力文字列を行に分解せずそのまま扱う方が高速になります。
次に、構文解析をやめて正規表現によるパターンマッチで済ませるようにします (list3-1)。 構文解析といっても大したことをやっているわけではないのですが、それっぽいことをしているので、この部分をパターンマッチに置き換えます。
list3-1: 構文解析をせず、パターンマッチで済ます (myeruby3.rb)
変換後の Ruby スクリプトは先ほどのと同じですので省略します。
ベンチマーク結果は table3-1 の通りです。 これを見ると、変換部分が 70% 以上高速化され、全体として C 実装 eruby より速くなったことが分かります。
table3-1: ベンチマーク結果
処理系 | 変換部分(sec) | 実行部分(sec) | 合計(sec) |
---|---|---|---|
C 実装 eruby | 1.390 | 13.958 | 15.348 |
ERB | 27.963 | 14.849 | 42.812 |
MyEruby1 (initial implement) | 79.210 | 13.320 | 92.530 |
MyEruby2 (no split lines) | 9.937 | 8.408 | 18.345 |
MyEruby3 (pattern matching) | 5.738 | 8.466 | 14.204 |
2回目の改良で、早くも目的は達成されました。 もちろん変換部分だけをみると C 実装 eruby にはかなわないのですが、変換後の Ruby スクリプトの実行が大幅に高速化されているため、全体としては C 実装 eruby よりも速くなっています。
このように、構文解析をまじめに行うかわりにパターンマッチで済ませることができると、大幅に高速化できる場合があります。 例えば XML ファイルからデータを取り出すときに、XML パーサを使うかわりにパターンマッチで済ませられないか検討するとよいでしょう。
次に、正規表現をチューニングします。 正規表現のチューニングでは、速い正規表現を使うことも大事ですが、それ以上に遅い正規表現を使わないことが大事です。
先ほどのプログラムだと、カッコを使ったグルーピングが 3 つありました。このうち 2 つめと 3 つめはマッチする文字列が短いですが、1 つめはかなり長い文字列にマッチします。
実は正規表現のグルーピングは、長い文字列にマッチするととたんに遅くなることが分かっています。 そこでこの部分を改良し、正規表現のグルーピングを使わずに自力で切り出してくるようにします (list4-1)。 グルーピングの数が 3 つから 2 つに減っていることに注意してください。 プログラムは長くなってしまいますが、高速化とのトレードオフです。
list4-1: 正規表現のグルーピングを避けて自前で切り出す (myeruby4.rb)
変換後の Ruby スクリプトに変更はありません。
ベンチマーク結果 (table4-1) を見ると、変換部分が確かに高速化されていることが分かります。
table4-1: ベンチマーク結果
処理系 | 変換部分(sec) | 実行部分(sec) | 合計(sec) |
---|---|---|---|
C 実装 eruby | 1.390 | 13.958 | 15.348 |
ERB | 27.963 | 14.849 | 42.812 |
MyEruby1 (initial implement) | 79.210 | 13.320 | 92.530 |
MyEruby2 (no split lines) | 9.937 | 8.408 | 18.345 |
MyEruby3 (pattern matching) | 5.738 | 8.466 | 14.204 |
MyEruby4 (optimized regexp) | 3.620 | 8.356 | 11.976 |
正規表現や SQL のように宣言的な記述を行うようなものはどれも、使うだけなら簡単ですが、中の仕組みがブラックボックス化しているため、チューニングが困難です。 その分、チューニングの余地が大きい箇所でもあります。 興味のある人は正規表現の動作コストをいろいろ調べてみてください。
次に、メソッドをインライン展開することで、メソッド呼び出しの回数を減らしてみます (list5-1)。 Ruby のメソッド呼び出しはそれなりのコストがかかるので、これを減らすことは一定の効果があります。
ただし、高速化よりもプログラムの保守性を優先するようにしてください。 今回はそもそも別メソッドにしなくてよいケースでしたのでインライン展開してよかったのですが、実際のプログラムでは必ずしもそうとは限りませんので注意してください。
list5-1: メソッドをインライン展開 (myeruby5.rb)
今回も変換後の Ruby スクリプトに変更はありません。
ベンチマーク結果 (table5-1) を見ると、変換部分が 15% 以上高速化していることがわかります。
table5-1: ベンチマーク結果
処理系 | 変換部分(sec) | 実行部分(sec) | 合計(sec) |
---|---|---|---|
C 実装 eruby | 1.390 | 13.958 | 15.348 |
ERB | 27.963 | 14.849 | 42.812 |
MyEruby1 (initial implement) | 79.210 | 13.320 | 92.530 |
MyEruby2 (no split lines) | 9.937 | 8.408 | 18.345 |
MyEruby3 (pattern matching) | 5.738 | 8.466 | 14.204 |
MyEruby4 (optimized regexp) | 3.620 | 8.356 | 11.976 |
MyEruby5 (inline method) | 3.091 | 8.320 | 11.411 |
変換部分の時間は、pure Ruby でありながら C 実装 eruby の 2 倍強で済んでおり充分高速といえますので、あとは実行部分の改善を図っていきます。
次に、変換後の Ruby スクリプトにおいて、文字列バッファではなくて配列バッファを使ってみます (処理系のプログラムはmyeruby6.rb)
変換後の Ruby スクリプト (list6-1) では、今までだと複数回の String#<< メソッドの呼び出しが必要だったのが、配列バッファを使うと 1 回の Array#push() メソッドの呼び出しで済ませることができます。 このため、メソッド呼び出しの回数を減らすことができ、変換後の実行部分が高速化されます。
list6-1: String#<< のかわりに Array#push() を使い、メソッド呼び出しを削減する
ベンチマーク結果 (list6-1) を見ると、実行部分が高速化し、全体で 10% 弱速くなっていることが分かります。
table6-1: ベンチマーク結果
処理系 | 変換部分(sec) | 実行部分(sec) | 合計(sec) |
---|---|---|---|
C 実装 eruby | 1.390 | 13.958 | 15.348 |
ERB | 27.963 | 14.849 | 42.812 |
MyEruby1 (initial implement) | 79.210 | 13.320 | 92.530 |
MyEruby2 (no split lines) | 9.937 | 8.408 | 18.345 |
MyEruby3 (pattern matching) | 5.738 | 8.466 | 14.204 |
MyEruby4 (optimized regexp) | 3.620 | 8.356 | 11.976 |
MyEruby5 (inline method) | 3.091 | 8.320 | 11.411 |
MyEruby6 (array buffer) | 3.139 | 7.381 | 10.520 |
なお配列を使って高速化されるのは、Array#push() メソッドが複数個の引数をとることができるおかげで全体のメソッド呼び出しを削減できるからです。 残念ながら String#<< メソッドや String#concat() メソッドは引数を 1 つしかとれないので、メソッド呼び出しを削減することには使えません。
しかし、文字列バッファでは別の方法を使ってメソッド呼び出しを削減できます。 これを次に見てみます。
配列バッファではなく文字列バッファを使う場合は、文字列中の式展開を使うことで String#<< メソッドの呼び出しを減らすことができます (list7-1)。
式展開とは、Ruby の文字列リテラルの中に任意の式を埋め込むことができる機能です。 Perl や PHP でも似た機能がありますが、これらの言語では埋め込めるのが基本的に変数名のみに限定されているのに対し、Ruby ではメソッド呼び出しや四則演算など任意の式を埋め込むことができます。
サンプルを見れば分かるように、変換後の Ruby スクリプトにおいて従来だと文字列結合のメソッド呼び出しが複数回必要だったのが、式展開を使うと 1 回で済むようになるため、変換後の実行部分を高速化できます。
list7-1: 文字列中の式展開を使って、String#<< メソッドの呼び出しを削減する
またこのサンプルでは「% 記法」を使っています。
「%Q...
」は「”…“」と同じ意味ですが、文字列リテラルを囲むのに「」以外の任意の記号を使うことができます。
「"」で文字列リテラルを表すと、HTML中にも「"」が頻出するため、そのエスケープ処理のコストが馬鹿になりません。
そこで「% 記法」を使い、例えば「
」のようなHTML中に現れにくい文字を使うことで、エスケープ処理を避けています。
なお処理系のプログラムはmyeruby7.rb
ベンチマーク結果 (table7-1) を見ると、実行部分の速度は配列バッファを使う方法と比べてほぼ同じであることがわかります。 つまり、メソッド呼び出しを減らすことによる効果はどちらも同じだといえます。
table7-1: ベンチマーク結果
処理系 | 変換部分(sec) | 実行部分(sec) | 合計(sec) |
---|---|---|---|
C 実装 eruby | 1.390 | 13.958 | 15.348 |
ERB | 27.963 | 14.849 | 42.812 |
MyEruby1 (initial implement) | 79.210 | 13.320 | 92.530 |
MyEruby2 (no split lines) | 9.937 | 8.408 | 18.345 |
MyEruby3 (pattern matching) | 5.738 | 8.466 | 14.204 |
MyEruby4 (optimized regexp) | 3.620 | 8.356 | 11.976 |
MyEruby5 (inline method) | 3.091 | 8.320 | 11.411 |
MyEruby6 (array buffer) | 3.139 | 7.381 | 10.520 |
MyEruby7 (interpolation) | 3.004 | 7.411 | 10.415 |
式展開を使ってメソッド呼び出しを削減する方法は、Ruby の特徴をうまく利用した、Ruby ならではの方法といえます。 逆にいえば、Ruby 以外では利用できない方法ともいえます。 他の言語では、配列バッファを使う方法がいいでしょう。
この時点で、 C 実装 eruby と比べて約 1.5 倍高速化されました。 プログラムの工夫がいかに大事であるか、実感していただけましたでしょうか。
次に、変換後の Ruby プログラムをファイルにキャッシュするようにします (list8-1)。 今までは、ファイルを読み込んで毎回 Ruby スクリプトへ変換していましたが、変換後の Ruby プログラムをファイルにキャッシュすれば、この変換コストをほぼゼロにすることができます。
list8-1: 変換後の Ruby スクリプトをファイルにキャッシュする (myeruby8.rb)
ベンチマーク結果 (table8-1) を見ると、変換部分のコストがほぼゼロになり、全体で 20% 以上高速化されたことかわかります。
table8-1: ベンチマーク結果
処理系 | 変換部分(sec) | 実行部分(sec) | 合計(sec) |
---|---|---|---|
C 実装 eruby | 1.390 | 13.958 | 15.348 |
ERB | 27.963 | 14.849 | 42.812 |
MyEruby1 (initial implement) | 79.210 | 13.320 | 92.530 |
MyEruby2 (no split lines) | 9.937 | 8.408 | 18.345 |
MyEruby3 (pattern matching) | 5.738 | 8.466 | 14.204 |
MyEruby4 (optimized regexp) | 3.620 | 8.356 | 11.976 |
MyEruby5 (inline method) | 3.091 | 8.320 | 11.411 |
MyEruby6 (array buffer) | 3.139 | 7.381 | 10.520 |
MyEruby7 (interpolation) | 3.004 | 7.411 | 10.415 |
MyEruby8 (file caching) | 0.662 | 7.360 | 8.022 |
またキャッシュを使えば、C 実装 eruby を使う必要がまったくないことも分かります。 C 実装 eruby によって速くなるのは Ruby スクリプトへの変換部分だけですが、キャッシュを使えばこの変換自体を省くことができるので、わざわざ C 言語を使ってまで高速化する必要はなかったわけです。
キャッシュを使う方法は、ERB にも適用可能です。 ERB での変換コストはとても高いので、ERB を使った CGI プログラムを書いている人は、ぜひキャッシュを使いましょう。
次に、文字列を eval するのをやめて、関数を定義して実行するようにします (list9-1)。
文字列を eval する方法では、Ruby パーサが文字列を解析して構文木を生成するコストが毎回発生します。 関数にして実行すれば、このコストをほぼなくすことができます。
list9-1: evalの代わりに、関数に変換して実行 (myeruby9.rb)
ただし、この方法で効果があるのは、ひとつのプロセス中で同じ eRuby ファイルを何度も実行する場合です。 Web アプリケーションでいえば、mod_ruby や FastCGI では効果がありますが、CGI では効果はないでしょう。
ベンチマーク結果 (table9-1) を見ると、約 1.5 倍高速化されたことがわかります。 つまり、eval だと全実行時間のうち約 3 分の 1 がプログラムのパースに費やされていることになります。
table9-1: ベンチマーク結果
処理系 | 変換部分(sec) | 実行部分(sec) | 合計(sec) |
---|---|---|---|
C 実装 eruby | 1.390 | 13.958 | 15.348 |
ERB | 27.963 | 14.849 | 42.812 |
MyEruby1 (initial implement) | 79.210 | 13.320 | 92.530 |
MyEruby2 (no split lines) | 9.937 | 8.408 | 18.345 |
MyEruby3 (pattern matching) | 5.738 | 8.466 | 14.204 |
MyEruby4 (optimized regexp) | 3.620 | 8.356 | 11.976 |
MyEruby5 (inline method) | 3.091 | 8.320 | 11.411 |
MyEruby6 (array buffer) | 3.139 | 7.381 | 10.520 |
MyEruby7 (interpolation) | 3.004 | 7.411 | 10.415 |
MyEruby8 (file caching) | 0.662 | 7.360 | 8.022 |
MyEruby9 (define method) | 0.001 | 5.105 | 5.106 |
この結果から想像できるように、パース後の構文木をファイルにキャッシュできると、関数にすることなくeRubyを高速化することができるはずです。 しかし残念ながら、現在の Ruby ではそれができません。 例えば Python だとパース後の (構文木ではなく) バイトコードをファイルに保存することができるので、関数にすることなく、また CGI でも、eval を高速に実行できます。 現在開発中の Ruby 1.9 ではバイトコードインタプリタになるので、バイトコードをファイルに保存できるようになれば、Python と同じように eval が大幅に高速化できるでしょう。
なお関数に変換するかわりに、Proc オブジェクトに変換して instance_eval() で実行する方法でも、同じように高速化できます(個人的にはこのほうが好みです)。興味のある人は試してみてください。
今回行った高速化の方法は、大きく3つの原則に分類できます。
1つめは__余計な処理をしない__ということです。今回でいうと次の方法が当てはまります。
2つめは__メソッド呼び出しを減らす__ということです。今回でいうと次の方法が当てはまります。
3つめは__正規表現を見直す__ことです。今回でいうと次の方法が当てはまります。
言語や環境によって個々の方法は変わっても、これらの原則は普遍的に通用します。 細かいテクニックを覚えることよりも、こういった普遍的な原理や原則のほうを大事にしてください。
本稿での内容は、Erubis という eRuby 処理系に反映されています。 それも含めて、他の言語で使われているテンプレートエンジンでベンチマークをしてみました (ベンチマークの内容は本稿で使用したのと同じものです)。 その結果が table10 です。
table10: ベンチマーク結果
言語 | ツール | 時間(sec) |
---|---|---|
Ruby | MyEruby7 (interporation) | 10.42 |
Ruby | MyEruby8 (cached) | 8.02 |
Ruby | MyEruby9 (def_method) | 5.11 |
Ruby | eruby | 15.32 |
Ruby | ERB | 42.67 |
Ruby | Erubis | 12.78 |
Ruby | Erubis (cached) | 9.15 |
Ruby | Erubis::Fast (cached) | 8.22 |
Perl | Template-Toolkit | 26.40 |
Python | Django | 50.55 |
Python | TurboGears(Kid) | 344.16 |
Java | Velocity | 13.24 |
使用したソフトウェアのバージョンは次の通りです。
ここで大事なのは、どれが速くてどれが遅いかということではなく、もちろん「○○という機能がある/ない」という話でもなく、__言語の速度がそのままアプリケーションの速度につながるわけではない__ということです。
Java が速いからといって、Java で作られたアプリケーションも速いとは限りません。 Ruby が遅いからといって、Ruby で作られたアプリケーションも遅いと決めつける必要はありません。 少なくとも、Reflection を多用したような__動的な Java プログラムよりも素の Ruby プログラムのほうが速い__です。
また eRuby は Velocity や JSP より速くできる2ので、Web アプリケーションのビュー層に限っていえば、Java で性能要件が満たせられるなら Ruby でも満たすことができるはずです。 今回のデータを見れば、10,000 ページ生成するのにノート PC で 10 秒もかかってないわけですから、ビュー層としては充分な速度でしょう。 つまりスクリプト言語でも工夫次第で十分な速度がだせるのです。
アプリケーションの速度は、アルゴリズムやデータ構造、使用するライブラリやデータベースなど、様々な要因が絡んできます。 言語の速度は、アプリケーションの速度を決定する要因の 1 つでしかなく、それよりも他の要因の方がずっと大きいということを知っておいてください。
本稿では eRuby の処理系を題材にとり、C言語 による拡張モジュールより高速 な pure Ruby プログラムを作成しました。 速度は、キャッシュなしで約 1.5 倍高速、キャッシュありなら約 2 倍高速、さらに関数化すれば約 3 倍高速になりました。 また他言語でのライブラリと比較しても、圧倒的に速いことがわかりました。
繰り返しになりますが、本稿でいいたかったことは、__言語の速度とアプリケーションの速度は別物であり、アプリケーションの高速化には言語の速度よりも知識やアイデアの方が重要__だということです。 アプリケーションの速度を決定する要因はいくつもあり、言語はそのうちのひとつに過ぎず、かつその影響度は一般的に考えられているよりも低いのです3。
スクリプト言語では最高速度を得ることはできませんが、たいがいのシステムが必要とする速度は十分得られます。 あなたが開発したアプリケーションが遅いとしたら、ほとんどの場合においてそれは Ruby やスクリプト言語のせいではなく、あなた自身のせいです。 安易に言語のせいにするまえに、自分のプログラムを見直してみましょう。 またスクリプト言語を使うなら、どれだけ速いかにこだわるのではなく、自分が必要とする速度がでればそれでよいという考えを持ちましょう。
なおテンプレートエンジンの速度に興味がある方は、Tenjin の Web ページに様々な言語のテンプレートエンジンを使ったベンチマーク結果が公開されていますので、そちらをご覧下さい。
本稿がスクリプト言語の発展に役立てば幸いです。