Written in Japanese(UTF-8)
2014.8.30
INASOFT


/トップ/いじくるつくーる/ダウンロード/WebHelp/ヘルプトップ/

本ソフトウェアの開発は終了しています。ヘルプに記載されている情報も古いものになっています。


あとがき2



〜R-Scriptを作り終えて〜


2003. 3.12



 今、朝の7:00を回ったところです。
 何を隠そう、昨晩は寝ずに、この「いじくるつくーる」のR-Scriptのドキュメントを書き上げたわけで、自分で自分を誉めたいです(笑)。

 そんなわけで(どんなわけで?)、「いじくるつくーる」とR-Scriptを作るきっかけ等などについて、語ってみます。


■卒業研究

 2002年6月。
 大学生活も4年目に突入。習得単位数が足りているので、あとは卒業研究に精を出せばよいという段階になった。

 まぁ、学部4年生なわけで、卒業研究をして、卒業論文を書いて、発表しなければならない。
 それが、まっとうな学部4年生なわけだ。

 で、僕の所属しているゼミではどんな研究をするのかというと、

 ・新しく、実用的なプログラムを作成する
 ・それについて、発表する

 というもの。
 特に「これをやれ」という指示がないのが、自由なのか、やりにくいのか…。まぁ、この当時はなんとも言えなかったわけだが、今からしてみれば、そのおかげでずいぶん楽しい1年間を送ることができたわけだ。

 で、問題なのは、新しく実用的なものを作ればいいというだけではない。
 何が新しくて、何が実用的なのかを、きちんと論理立てて、数学的に証明していかなければならないのだ。

 そりゃ、数学科に所属していますからね。
 まぁ、論文というのは、概してどこの学部でもそういうものだけど…。

 で、卒業研究の題材を決める上で、僕の中にあった考えは、「どうせ、こういう指示が与えられているのなら、自分のための将来に役立つような研究をしよう」ということだった。

 というわけで、このときに自分の中で問題とされている2つのことについて、考えることにした。それは、

 ・Windows上の自殺プログラム問題
 ・いじくるツールの開発効率が悪い問題

 前者は、わかりやすくいえば「アンインストールプログラム」はアンインストールプログラム(自分)自身を削除できなくてごみになってしまう、というもの。当時知られていた解決策は、いったんWindowsを再起動して、再起動したときについでに削除するという方法なのだが、ただでさえ再起動が多いと言われているOSの再起動回数をさらに増やしてどうするという考えがあったので、この方法は気に入らなかった。

 というわけで、解決策を模索していたのだが、なんと、バッチファイルは自殺できるということがわかり、ほんの数日で解決してしまった。ほんの数日で解決することが卒業研究になったら、何のために4年間大学に通っていたのだかわかったもんじゃないので(いや、大学卒という称号を得るため、という話もありますけどね)、これは題材としては不適切ということになった。

 そこで、題材は後者の「いじくるツールの開発効率が悪い問題」ということになった。

 当時、「いじくるツール」には、毎月多量の機能追加要望が寄せられていて、それに応えていくのがとても大変だった。
 プログラムの改修を機械的に簡単に行う問題「いわゆるリファクタリング(再構成)」というのは、いくつかモデル化されているものがあったので、それの上の乗っけてプログラムの改修を行うような方法を考えたのだが…なかなか思いつかない。

 と、そんなとき、担当の先生から、

 「それなら、専用のスクリプト言語を作って、その上でプログラムを再構成してみたら?」

 と言われた。

 新しいスクリプト言語を作って、既存のプログラムを書き直したら、再び再構成のしなおしでややこしいことになってしまうのではないか…とも思ったが、現状でもすでにゴチャゴチャのプログラムになっていて、これ以上ややこしくなることはないだろう、という考えと、「インタプリタの作成」というのが、学部4年生の卒業研究の題材にはぴったりかなぁと思ったことで、これに決めることにした。


■インタプリタ

 最終目標は、新しいスクリプト言語を作って、その上でレジストリ編集を行うアプリケーションを構築すること。
 で、新しいスクリプト言語にはC言語にはない新しい特徴があって、アプリケーションの構築の能率が上がること。

 これだ。

 とりあえず、最終目標が決まったところで、どういうふうにそれを実現しなければならないかを考えるところだが、まぁそんなにすぐに思いつくものでもないだろうから、とりあえず、インタプリタの作成に取り組むことにした。

 さて、世の中には便利なツールがあるもので、lexとyaccという、文法ルールをある決まりに従って記述しておけば、ソースプログラムの文法解析を行うCソースコードを自動的に生成してくれるツールがあるのだ。

 まぁその過程で、正規表現のお勉強をしたり、yaccの衝突回避のために夜も眠らず努力をしたり…な〜んて話もあるが省略。とりあえず、この便利なツールのおかげで、インタプリタの作成は簡単に終わった。

 簡単とはいえ、冷や汗ものだったのが、yaccの文法ルールがメモリオーバーフローを起こしたこと。
 「これ以上のルールは追加できない」ということだ。

 まだまだインタプリタを完成させるには文法が足りないというのに、これ以上の文法が追加できない、と言っているんだから、困ったものだ…。どうしようか…と思っていたら、関数の引数の表現を簡単にすることで解決できた。

 具体的には、0個以上の引数をargs, 1個の引数をarg とすると、


func : symbol '(' args ')'


args : arg ',' args

| arg
| (なし)

arg :数

:文字列




 という表現である。
 僕は、小さいときから「手続き型」のプログラムばかり作っていたので、こういう、帰納的な定義をするような記号記述というのことに経験が浅いせいか、上のような発想が浮かばないのである。
 で、まぁ問題は解消し、文法規則の生成は無事に終了。夏の終わりのことだった。


■3つの段階と1つめの問題

 文法ルールができたところで、関数を作って、レジストリの操作を行わなければならない。
 が、C言語のマネをしただけでは、結局、「いじくるツール」の開発効率となんら変わらないものができてしまう。
 (まぁ、文法側で文字列を柔軟に操作するための文法を作ってあったから、ある程度はCよりはやりやすいが)

 そこで、現状のVisual C++によるプログラミングのどこが悪いのか、ということを、考えてみることになった。

 レジストリ編集アプリケーションというのは、必ず、

 1. レジストリからデータを読み取り、コントロール(編集ボックスやチェックボックス)に入れる。
 2. ユーザーがコントロールで編集を行う。
 3. 編集結果をコントロールからレジストリへ書き込む。

 という3段階を踏むことになっている。

 この3段階は、「各段階で、必要なコントロールに対するすべてのコードを書く」必要がある。つまり、2つ以上のコントロールをダイアログ上に載せていれば、{1に対する2つ以上のコントロールのためのコード群}{2に対する2つ以上のコントロールのためのコード群}{3に対する2つ以上のコントロールのためのコード群}というのが、プログラム全体のおおまかな構成となる。

 1つのダイアログにはいくつもの設定が混在しているのだから、2つ以上のコントロールに対するコードがプログラム中に存在することは必定で、その結果、1つの設定に対するコードが、ソースコード中の少なくとも3個所に分散してしまうことになる。

 それゆえ、プログラマはソースプログラムの該当する3個所を探しながら、機能追加したり、修正したり、場合によっては削除した利をしなければならないので、レジストリ編集アプリケーションのプログラマは苦労するわけである。

 これが、開発効率を悪化させている1つの原因であることがわかった。

 次に、レジストリを用いる設定系アプリケーション固有の問題というのがあった。


■2つめの問題

 いきなりだが、レジストリ編集アプリケーションというのは異質な存在である。
 まぁ、そんなこと知っていると言われたらそれまでだが、ここで論じるのは、次のような観点からである。

 一般のアプリケーションがレジストリに設定を保存・呼び出しする場合、自分自身の設定を保存しているレジストリキー1つから、多量の設定情報(エントリ)を読み書きする

 それに対し、「レジストリ編集アプリケーション」は、Windowsのさまざまな場所から多量の設定情報を読み出すために、「たくさんのレジストリキーを開き、たくさんのエントリを読み書きする」作業をしなくてはならない。

 これがプログラムにどのような影響を及ぼすかというと、一般のアプリケーションなら、

  「キーを開く」→「値を読む」→「値を読む」→「値を読む」→「値を読む」→「値を読む」→「閉じる」

 で済むのに対し、レジストリ編集アプリケーションは、

  「キーを開く」→「値を読む」→「閉じる」→「キーを開く」→「値を読む」→「閉じる」→「キーを開く」→「値を読む」→「閉じる」→「キーを開く」→「値を読む」→「閉じる」→「キーを開く」→「値を読む」→「閉じる」

 としなければならないのだ。
 これが、プログラマを苦労させている2つめの問題であることがわかった。


■問題解決

 2つめの問題解決は、「キーと値の組み合わせを指定して読む/書く」という関数を作ることによって、容易に解決した。
 難解だったのは、1つめの問題の方である。

 プログラムの、1つの目的のためのコードが3個所に分散してしまうのは、プログラムの流れからいって、しかたの無いことである。読んで、編集して、書いて、という過程は、「複数のレジストリ設定の読むコードの集合」「複数のレジストリ設定の編集するコードの集合」「複数のレジストリ設定の書くコードの集合」というまとまりからは、どうしても離れられない。

 と、よくよく考えると、手続き型の処理にこだわっているから、「プログラムの流れ」に縛られてしまうわけで、例えば「メニュー定義」のように、流れとは関係のない記述ができれば、上のような3つの段階にコードが分散してしまうことはない。

 そこで、「メニュー定義」による実装を考えた。
 これならば、必要な項目の名前と関連するレジストリキー/エントリを記述しておくだけで、必要な操作を自動的に行ってくれるようなプログラムになってくれるはずである。

 ところが、この解決策はすぐに行き詰まった。

 Windowsのレジストリ設定というのは、「1つのレジストリと1つの設定が思惑通りにしっかり対応する」というように都合のよいものばかりではない。

 例えば、すべて設定が、設定値1で動作ON、設定値0で動作OFF ならよいのだが、中には、設定値2でONだったりするものもある。
 このような例は序の口で、実はもっと、複雑な例はたくさんある。
 こういった例を、メニュー記述で書こうとすると、そのパターンは膨大なものになり、記述のためのルールを作るのは大変なことになる。こういうのはやっぱり、手続き型のプログラムとして書いたほうが、プログラムを作る効率が良いに違いない。

 では、手続き型で、かつ、メニュー記述のようなことをするにはどうしたらいいか。
 そこで、都合の良いものだけ、メニュー記述の様にスクリプトのコードに記述する方法を考えた。


■関数のようで関数でない

 数学科にとって、関数といえば、y = f ( x ) のあの関数なわけだが、プログラマにとっての関数は、そういうものではなく、手続き群である。なので、「関数」というよりは「関数(らしきもの)」といったほうが正確かもしれない。

 で、この関数(らしきもの)に対して、メニュー記述に必要な要素を全部放り込んでやることを考えた。

 メニュー記述だけでは不完全なものは、メニュー記述に必要最低限なものだけを関数(らしきもの)に放り込んで、残りの要素は分離して書けるようにした。

 と、このようにして R-Script ができがったわけだ。
 (ちなみに、このネーミングは nScript っていうのからとっているらしい…)


■切り捨てたこと

 さて、今回の過程で切り捨てられたことがある。
 それは、ダイアログの美しさだ。

 プログラマの苦労はそいだわけだが、その分、ダイアログが美しくなくなり、ユーザーにとっては使いにくいものになったかもしれない。

 でもまぁ、今回の卒業研究はユーザーの利便性を追及するものではないということと、美しさについては今後の課題ということにして、インタプリタ側の改良だけで済む(スクリプト側の改良は最低限のもので済む)だろうという楽観から、今後の課題ということで決定した。


■文法の追加

 さて、できあがったオリジナルのスクリプト言語で、さっそく、「いじくるツール」(ver.6)の全機能を移植してみることになった。

 たしかに、機能の移植をしてみると、全体的に行数は減った。
 でも、なんだかとても疲れた。

 プログラムを見渡す手間は減った。
 開く→読む→閉じる の3段階処理も減った。

 なのに、疲れた。
 なぜだ…。

 考えてみると、この当時のスクリプトのフロー制御には if と goto しかなく、分岐をするたびに新しいラベル名を考えたり、ラベルの対応を頭の中で記憶したり…、と、結局、R-Scriptにより解決した問題がある一方で、フロー制御という新しい問題が発生したせいで、苦労の量は減っていないことがわかった。

 というわけで、構造化プログラミングというものに挑戦することになった。

 まずは、if 〜 else で、中括弧を使って、分岐をブロック化する処理を考える。

 途中は省略するが、最終的に思いついた(というか、アドバイスを受けてそうした)のが、プログラムを読み込んだ時点で、中括弧を見つけたら、それを内部的にgoto文とラベルに置き換えるというもの(つまり、インタプリト前にプリインタプリトしておく)。

 インタプリタ自体はほとんど改良せずに、テキストの書き換えという単純作業で済むから、楽だ。

 …と思ったが、どうやって、そのgoto文とラベルの対応をとったらいいか…。
 だいたい、ifやelseが単純に登場するだけなら良いが、もしネストしていたらどうしたらいいのか…。

 で、ここで考えたのが、中括弧がネストしているということは、入れ子構造なわけで(そのまま)、ということは、スタックの構造と似ているのではないか、ということだ。

 中括弧の開始が出てきたら「プッシュ」(push)して、中括弧の終わりが出てきたら「ポップ」(pop)する。
 これで、どこがどう対応しているのか、わかった。

 で、その後、同じような発想で if 〜 else if 〜 else を作ったり、while を作ったりして、現在の形になった。
 ここまで実装すると、goto を一切使わない「構造化」プログラムが組めるようになったわけで、ずいぶんとプログラムを組みやすくなった(また、見直しやすくなった)。「構造化」のありがたみを存分に味わうことができたなぁと思い、感動した。


■卒論発表

 さて、能率の良いプログラムが組めることは、体験的にわかった。
 で、問題は、これをどうやって「数学的に論理的に説明するのか」という問題である。

 もちろん「自分が使いやすいと思ったから、これは使いやすい。終わり」ってなこと言ったら、来年も在学生でいることは間違いないない。そこで、数値的にプログラムが組みやすくなったことを示すことになった。

 まず1つ着目したのが、ソースコードの行数。
 3つの段階を1回でかけるようにしたのだから、コードのサイズは33%近くに収まっているはずである。
 で、実際、ソースコードの行数比較では、R-Scriptで作ったプログラムが、Visual C++のプログラムの27%まで小さくなっていることを確認できた。

 それからもう1つが、「同じエントリ名が何回登場しているか」という比較。エントリ名が複数回登場しているということは、プログラムがそれだけ分散しているということだ、という考えだ。これでも、とてもよい成績を収めることができた。

 というわけで、無事、課題に合格をもらい、卒業が確定した。


■卒業論文とマニュアルと完成

 さてと、残された大事な仕事は3つ。

 まず、卒業論文の作成。
 まぁ、提出せずに逃げても卒業させてもらえるのだが、それはとても後味が悪い。なので、作らなければならない。これは、目下作成中である。
 
 次に、マニュアルの作成。これは、論文中で「マニュアル参照」と逃げている個所があるので、マニュアルも完成させなければならないということだ。というわけで、たった今、マニュアルの作成が終わったところで、こうしたしこしこあとがきを書いている、というわけ。こんなもの書いているひまがあったら、「卒業論文」を完成させよと言われそうなところだが、こういう根性が、INASOFTを毎日更新させ、ある程度のアクセス数を確保しているのだなぁとおもうと、こういう思考回路も保護せにゃならんなと自分で自分を過保護にしつつ、まぁ、今日の午後から作ろうか。

 最後に、プログラムの完成。
 最初に述べたとおり、この課題は、自分にとって「実用的なもの」である必要がある。
 そんなわけで、自分の就職までに「一般公開」しなければならないわけだ。

 まぁ、ベータ版の公開は去年から行われていて、つい昨日、全機能の移植(一部、危険性のために排除されたモンは除く)が終わったので、そろそろRelease Candidateが出せるかなぁという状態になった。

 「あとがき」なので、あとに書くべきだが、ちょっと早めの「あとがき」でした…。


2003年 3月12日
大学にて(823)



 目次へ


※このページは、ソフトウェアに付属のヘルプファイルをWeb用に再構築したものです。大部分に自動変換を施しているため、一部は正しく変換しきれずに表示の乱れている箇所があるかもしれませんが、ご容赦下さい。また、本ドキュメントはアーカイブドキュメントであり、内容は「いじくるつくーる」最終公開時点、あるいは、それより古い時点のものとなっております。一部、内容が古くなっている箇所があるかと思いますが、あらかじめご了承下さい。
※このページへは、自由にリンクしていただいてかまいません。
このページに関するご意見の受け付けは終了しています。

/トップ/いじくるつくーる/ダウンロード/WebHelp/ヘルプトップ/