本章では、論旨の主張のために開発する作成物(新しいメトリクスツール)と、その開発に必要となる道具(概念・環境・技術要素)について述べる。
本章では、論旨の主張のために開発する作成物(新しいメトリクスツール)と、その開発に必要となる道具(概念・環境・技術要素)について述べる。
これまでに述べた論旨二点の主張を目的とするソフトウェアの開発を行う。 「可食化の実現」を本願としながら、「枠組みの利用」により再利用性の高いメトリクスツールを開発することが目標となる。 従って、枠組み「メトリクスグラフ」の構成要素である「ノード」や「アーク」に相当する仕組みを、それぞれ可能な限り独立した機能として実現したい。 また最終的には、ソフトウェアプログラムからスパゲッティを得られるように、ツールを仕上げなければならない。
本開発では、大きく分けて二つの機能を実現しなければならない。 また、それらは独立しているものの、運用時には共に協調して動作するものであるから、一方の設計に大きな変更があった場合には、もう一方にも影響を及ぼす可能性がある。 そのリスクを吸収するというねらいから、アジャイルソフトウェア開発の一であるXP(エクストリームプログラミング)の考え方を参考に開発を行う。 (cf. 参考文献[5])
2012年10月25日から本開発を始動し、アイデアの捻出や反復開発を経て、要求事項の達成を年内(2012年12月末日まで)に遂げ、開発を収束させるものとする。
上記のメトリクスツールを開発するにあたり、その実現に向けて二つのアイデア(初期計画)を用意していた。それぞれを示しておこう。
入力を出力に変換する関数のようなもの(写像役。以降「メトリクスプラグイン」とも呼ぶ。)を用意しなければならないが、それなら、その関数を何かしらのプログラミング言語で実装すれば実現できよう。 しかし、「再利用性の高いツール」というコンセプトを考えると、特定のプログラミング言語に媚びることは芳しくない。 誰でも好きな言語を用いて、独自のメトリクスプラグインを開発できるようにしておきたいのだ。 ここで着想したのが、ソフトウェアプログラムのビルドツールとして広く用いられている「make」を転用することである。
「make」を利用しない場合は、プラグインの機能実装に使われる言語の違いをアーク側(繋ぎ役)が考慮しなければならないが、それではノードとアーク間に依存が生じ、独立した機能として実現できない。 そこで「make」を利用することにより、その言語の違いをプラグイン側で吸収すれば良いのだ。 つまり、メトリクスプラグインを利用するためのインタフェースとして「make」を転用するのである。
メリットはこれに留まらない。プラグインと繋ぎ役との間で共通の界面を利用できることはもちろん、プラグインと人間との間においても同じ効用が期待できるであろう。 それぞれの実装言語の使い方を知らずとも、ビルドツール「make」の扱いさえ理解しておれば簡単に扱えるのだ。 また、もはや「人間」に限定する必要もなく、プラグインと何か他のシステムとを連携させる場合も同様であるから、この統一されたインタフェースによる利益は大きいと言える。 これなら、再利用性に関するコンセプトを充分に満たしているはずだ。
なお、「メトリクスプラグイン開発ガイドライン」を付録資料[3]として添えておく。
メトリクスプラグイン(写像役)や写像元・写像先といった「ノード」を繋ぎ合わせることで、メトリクスグラフを表現することができるが、その繋ぎ合わせ(接続関係)を表現する方法として「SynapseScript」という言語を提案したい。 その紹介に先立ち、グラフに関する二つの議論を済ませておきたい。まずは多重グラフの表現方法について述べよう。
本来のグラフにおいて、ノードの接続関係を表現するには、接続したい二つのノードを一組として表現するだけで良い。その表現そのものがアークを表すのだ。 例えば、図3-3のグラフにおけるノードの接続関係を表すと、次のようになる。
(A,B) (A,C) (A,D) (B,C) (B,E) (C,D) (C,E) (D,E)
丸括弧やカンマに意味はない。(有向グラフであるから、括弧内のノードの順序には意味があるのだが、)大切なのは、二つのノードが一組として表されている、その構造である。 次に、図3-4のような多重グラフであればどうであろう。ある二つのノード間が、それぞれ別の二本のアークで接続されているなら、どのように表現すれば良いのか。
(A,B) (A,B)
これは失敗である。二つのアークを表現してはいるが、それらを区別することができない。 それなら、アークの両端点(ノードとの接続点)に対して識別可能なラベルを付した図3-5は、表現できるだろうか。
(A.x,B.y) (A.y,B.x)
あるノードAに付したラベルxは、ピリオドを挟んで「A.x」とすることで容易に表現できた。
第2章 理論と実施計画 - 理論(写像 メトリクスグラフ)で述べたとおり、メトリクスグラフもまた多重グラフ(多重有向グラフ)であるから、 ラベルが必要になる場合がある。その簡単な例として、除算(割り算)のメトリクスグラフを図3-6に示す。
除算を行うメトリクスプラグインを中心に、写像元(入力値)二つと写像先(出力値)三つが描かれている。 このように、多入力または多出力を持つノードは、各接続点にラベルを付けて識別可能にし、接続関係を表現できるようにしなければならない。
さて、残る一つは必ずしも議論を要することではないのだが、接続関係を表現する際の冗長性(無駄)を省くために考えた工夫があるので、ここで述べておきたい。
図3-7のグラフについて、その接続関係を素直に書き下ろせば、次のようになる。
(A,B) (B,C) (C,D)
連続する接続を表現したものであるが、一つの接続ごとに改めてノードを記さねばならず、やや冗長さを含む。これを次のように書き直せば、どうであろう。
(A,B,C,D)
重複した記述、つまり冗長性を排除できたことは自明である。 もちろん、その接続の意味合いによっては、分けて書くべき場合もあろう。大事なことは、この接続関係を記述する者に対して、表記の自由を与えることである。 一方を無闇に排除したりすべきではなく、どちらの表記も許容することが肝要である。表記の細事ついて考えを凝らすべきは設計者ではなく記述者なのだ。
これら二つの議論を踏まえ、メトリクスグラフを表現することに適した言語を設計・開発する。 わざわざ新しい言語を開発する必要はないだろうが、それでも言語開発に拘ったことには理由がある。 詳しくは「はじめに - 三つの興味(言語処理系に関すること)」を参照されたい。 それから、言語「SynapseScript」に関する詳細は「SynapseScript 言語マニュアル」をご覧頂きたい。(cf. 付録資料[4])
これまで触れなかったが、本開発にあたりその制限事項について簡単に付け足しておく。
初学者のプログラムの良し悪しを、と議論してきたが、実を言えば、そのいずれもC言語のプログラムを念頭に置いて展開してきた。 本来は、言語の違いも吸収してスパゲッティを出力したかったのだが、解析の方法を一意に定めるため具体的な言語を決めておくことにする。
プログラムを入力してスパゲッティを出力する、と述べてきたが、メトリクスツールで写像した結果をいきなり現実空間のスパゲッティに変換できるわけではない。 結果の数値を基に調理して初めて、目当てのスパゲッティを得ることができる。 つまり、今回開発するメトリクスツールは、「スパゲッティ」を出力するわけではなく「スパゲッティのレシピ」を出力するものである、と断りを入れておく。
SynapseScriptの言語処理系を開発しなければならないが、その実装を行う言語として「Smalltalk」を、またその処理系として「VisualWorks 7.8」用いる。 研究室にて普段からよく利用していたこと、理不尽な仕様が比較的少ないこと、プログラム走行時のテストが非常に容易であること、などを理由として採択に至った。
開発計画の通り短期的な開発の反復を基本として、また上記アイデアを基盤として、要求を充足する機能(ソフトウェア)の開発に取り組む。
開発する言語「SynapseScript」は、メトリクスグラフを表現するためのものであるが、可能な限り簡単な記法かつ読みやすい記法を採用したい。 実際に記法の具体案を十数例ほど用意し、吟味の上で一つに絞り込んだ。以下に例題グラフ(図3-8)と、そのグラフに対応するSynapseScriptの記述例を示そう。
sourceFile := File("/path/to/SourceFile.c"). headerFile := File("/path/to/HeaderFile.c"). metrics1 := Metrics("/path/to/Metrics1"). metrics2 := Metrics("/path/to/Metrics2"). metrics3 := Metrics("/path/to/Metrics3"). metrics4 := Metrics("/path/to/Metrics4"). transcript := Transcript(). resultFile := File("/path/to/ResultFile.txt"). sourceFile to: metrics1 to: metrics4.inputA to: transcript. sourceFile to: metrics2.source send: outputA to: metrics4.inputB. headerFile to: metrics2.header send: outputB to: metrics3 send: outputA to: metrics4.inputC. metrics3.outputB to: transcript. metrics3.outputB to: resultFile.
SynapseScriptは「ノードの定義文」と「アークの定義文」の二種類により構成される。示した例では、上部にノードの定義を、下部にアークの定義を記述している。 1行目は写像元となるノードを定義して、それ以後は、sourceFileという名称で扱うことを宣言している。 3行目は写像役(メトリクスプラグイン)となるノードを、8行目は写像先となるノードを定義して、それぞれの別名称を宣言している。10-13行目は、四つのノードを連続に接続することを表している。 sourceFileをmetrics1に入力し、その写像結果(出力)をmetrics4のラベルinputAに入力し、さらにその結果をtranscriptに入力している。
このように記法が定まったことから、字句解析用の正規表現と構文解析用のBNF(バッカス・ナウア記法)を用意した。これは言語マニュアルに添えてある。(cf. 付録資料[4])
さらにプラグインの設計を行い、これまで漠然としていた実現方法について具体的に検討した。 一つのディレクトリを一つのプラグインと見立て、そのディレクトリ内に具体的な写像の仕組みを格納し、ビルドツール「make」を経由して写像処理を実行できるようにする。 ここで写像の仕組みとは、makeの動作規則を定義するテキストファイル「Makefile」ならびに、具体的な写像処理を記述したプログラムのことである。 また、プラグインは幾つかの機能を実現しなければならないが、その方法としてmakeのターゲットを活用することにした。 makeは、端末上で「make」と入力すると動作するのだが、後部にターゲット名と呼ばれるものを付すことで実行する動作規則を変更することができるため、これを機能選択の仕組みとして利用するのだ。
それから、プラグインに入力値を引き渡す方法として、makeのマクロ定義を用いる。これが変数のような役割を果たすことで、プラグインへの値の入力を可能にする。 また、プラグインの写像結果の出力形式として、JSONを用いる。単純な記法で連想配列(名前と値の一組を要素とする配列)を表現できることから、ラベル付きの複数値を出力するメトリクスプラグインに適していると考えた。
以上の通り、SynapseScriptの言語設計とメトリクスプラグインの仕様設計を行い、実現に必要となる技術項目を洗い出すことができた。 ただし、この時点での言語設計及びプラグイン設計は変更される可能性があるということに注意されたい。(実際この後、仕様を変更することになる。) なお、第1期開発は、2012年10月25日から同年11月14日まで実施した。
言語の定義を終えたため、その言語処理系の実装を行う。 SynapseScriptで記述された表現を解析し、その構文木を得ることが目標となるが、これは既存の字句解析器生成系(lex/flex)や構文解析器生成系(yacc/bison)を用いることで容易に実現できる。 しかし、「はじめに - 三つの興味(言語処理系に関すること)」で述べた通り、処理系そのものにも興味を抱いていたことから、 字句解析器と構文解析器を独自に実装することにした。
第1期開発で用意した正規表現に基づいて図3-9に示すオートマトンを描き、字句解析を実装した。
構文解析の方法には幾つかの種類があるが、中でも以前から学びたいと考えていたLR構文解析(SLR(1))を実装した。
実装した解析器の動作を検証するため、次のサンプルプログラムを解析し、その結果(構文木)を図示することにした。その様子を図3-11に示す。 なお、このサンプルプログラムが表すメトリクスグラフの意味を考える必要はない。
transcript = Transcript("hoge"); transcript send: output to: transcript.input;
以上の通り、SynapseScriptの字句解析及び構文解析を実現することができた。 この開発に際して参考文献[10] [11]が大変役立った。 なお、第2期開発は、2012年11月14日から同年12月1日まで実施した。
Smalltalk上でSynapseScriptを解析し、得られたメトリクスグラフの写像関係に従ってメトリクスプラグインを利用することになる。つまり、Smalltalkからmakeコマンド(シェル)を利用できなければならない。 その仕組みは標準で提供されてはいるのだが、不具合や不都合(マルチバイト文字の問題)が存在するため利用できない。そこで別の手段として、SmalltalkからCを経由してシェルを利用することにする。
これはVisualWorksに付属しているDLLCCという仕組みにより、容易に実現することができる。なお、DLLCCについては参考文献[12]に詳しい。
Cの標準ライブラリに用意されている関数systemで、シェルのコマンドを実行することができるが、その実行結果(出力)を得られないという欠点がある。 そのため別の手段として、fork、pipe、dup2、execといったシステムコールを利用し、Cからシェルの子プロセスを立ち上げてパイプで連携することによって一連のシェルコマンドを実行することができた。
上記二項を合わせ「ShellInterface」及び「ShellInterpreter」と称して、Smalltalkからシェルの利用を実現した。
開発したソフトウェアはバグを含む。シェルの子プロセスの管理またはパイプの管理に不備があるようで、結果が応答されず、処理も終了しないことがある。 また、子プロセスに対するEOF(End Of File)の送信を実現できなかったため、EOFを受けるまで待機するコマンドを実行すると、そのコマンドの実行を止めることができない。
ひとまず、Smalltalkからのシェルの利用を実現することはできた。残されている種々の問題については、今後の開発で改善してゆくことが期待される。 なお、第3期開発は、2012年12月2日から同月12日まで実施した。
第2期開発の成果物を用いてSynapseScriptによる記述を解析できるが、そこで得た構文木に従ってメトリクスグラフを構築し、実際の写像処理を実行できるインタプリタを開発したい。 その開発にあたり、「SynapseScript」という名称の由来でもある「神経系」の構造を模して設計することにした。概念図を図3-13として示す。
ノードをニューロン(神経細胞)に喩えると、その神経線維(軸索と樹状突起)がちょうど繋ぎ役(コネクタ)となる。 ニューロンが神経線維を通して、他のニューロンに対して物質を伝達するように、ノードもコネクタを通して、他のノードに情報を伝達すれば良い。 伝達機能を持つオブジェクトを拵えた上で、構文木から神経構造(メトリクスグラフ)を構築すれば、再帰的に全ての写像を実行することができよう。
この考えに基づいて「SynapseInterpreter」という名称でインタプリタを開発した。 動作テストとして、簡単なメトリクスプラグインを作成し、それを用いるメトリクスグラフを記述・実行したところ、確かに設計通りに動作することが確認された。 なお、第4期開発は、2012年12月15日から同月20日まで実施した。
第4期開発の動作テストでは、メトリクスプラグインの試作を作り利用していた。 その際も特段の問題は起こらなかったため、メトリクスプラグインの開発方法を具体的に定めて、ガイドラインとして策定することにした。
ところが第6期開発に入って仕様の問題が発覚した。 メトリクスプラグインにデータを入力する際、makeコマンドにおけるマクロ定義として記述すると先に説明したが、入力データに特殊文字が存在する場合にエスケープ処理を施さねばならない。 また、そのエスケープはデータの受け渡しを行うごとに必要とされるため、「エスケープしたものをエスケープし更にエスケープする」といった複雑な処理を行う羽目になるのだ。
そして、そのエスケープの施し方は環境により異なるため、特定の環境で正しく動作しても、別の環境で動作するとは限らない。これでは、再利用性を高くするというコンセプトにも反しよう。 統一的なエスケープ処理で済ませるために試行錯誤を重ねたのだが、具体的な方法を見出だせなかったため、その代案として、エンコード規格の一つ「Base64」を援用することにした。 どのようなデータも、Base64でエンコードすることで「エスケープを必要としない文字列」に変換できるため、データの受け渡しが幾段に重なろうとも問題は起こらないのである。
本来ならデータをそのままの状態で扱い、その可読性を維持したかったのだが、重なるエスケープに頭を悩ませることこそ本質的ではないため、止むを得ず妥協に踏み切った。 ともあれ、ある種のメタ系(文字列で文字列を取り扱おうとする構造)の難しさを垣間見ることができ、学びとなった。 なお、第5期開発のうち、特に仕様策定は2012年12月21日のみ、また仕様再考とそれに伴う追加実装は同月29日から翌年2013年1月1日まで実施した。
ソフトウェアプログラムをスパゲッティとして可食化するために、その写像を表現したメトリクスグラフ(図3-15)を用意した。
また、このグラフを構成している各メトリクスプラグインについて、その実現方法を説明する。 ちなみにメトリクスプラグインは、開発者の好きな言語を利用することができる。そのことを示すため、以下に挙げるプラグインはそれぞれ異なる言語で実装している。
Cで記述されたプログラムを入力し、基本的な指標を得るためのメトリクスプラグイン。得られる指標値は以下の通り。
種別 | ラベル名 | 説明 |
---|---|---|
入力 | input | 解析対象のCプログラム。 |
出力 | linesOfCode | Lines of Code。プログラムの行数。effectiveLOCの値をそのまま採用する。 |
出力 | effectiveLOC | eLOCとも。空白行・コメント行・括弧のみの行を除いた行数。 |
出力 | physicalLOC | pLOCまたは物理LOCとも。単純な意味のプログラム行数。コメント行なども全て含む。 |
出力 | logicalLOC | lLOCまたは論理LOCとも。プログラムの構成単位「文」の数を意味する。 |
出力 | commentLines | コメント行数。 |
出力 | numberOfFunctions | 関数の宣言数。 |
出力 | averageLengthOfId | 識別子(変数名や関数名)の長さの平均。単位は文字。 |
出力 | indentDifference | 一行あたりのインデントのずれ具合。単位は文字。 |
出力 | standardIndent | 標準インデント量。そのプログラムで最も多く使われているインデント量。 |
出力 | commentRatio | コメント記述の割り合い。commentLines÷physicalLOCによって求める。 |
解析のために字句解析器・構文解析器が必要となるが、これにはflex及びyaccを利用して生成している。
以下は、「標準インデント量」と「インデントのずれ具合」の定義について述べる。 通常のインデントで半角スペースを四つ用いる場合、その「4」という数値が「標準インデント量」に相当する。 また「インデントのずれ具合」の説明するため、次の二図をご覧頂きたい。
図3-16及び図3-17に同じ内容のプログラムを示したが、それぞれインデントの整え方が異なる。 標準インデント量が「4」である場合、図中橙色の補助線に沿ってプログラムを記述するべきだが、右のプログラムはその補助線に沿っていない。 その補助線からずれている文字の数を全て数え上げ、単位行あたりの数値としたものが「インデントのずれ具合」である。
除算(割り算)を計算するメトリクスプラグイン。2入力3出力。中核となる実装言語はPerlである。
種別 | ラベル名 | 説明 |
---|---|---|
入力 | dividend | 被除数。割られる数。 |
入力 | divisor | 除数。割る数。 |
出力 | quotient | 商。 |
出力 | floorQuotient | 端数を切り捨てた商。 |
出力 | remainder | 剰余。割り算の余り。 |
関数ごとの行数から、スパゲッティをゆで上げた後の待ち時間を算出するメトリクスプラグイン。中核となる実装言語はAWKである。
種別 | ラベル名 | 説明 |
---|---|---|
入力 | input | 関数あたりの行数。 |
出力 | output | ゆで上げ後の待ち時間。単位は分。 |
識別子(変数名や関数名)の長さの平均から、スパゲッティ料理の名称や画像を得ることができるメトリクスプラグイン。中核となる実装言語はPHPである。
種別 | ラベル名 | 説明 |
---|---|---|
入力 | input | 識別子(変数名や関数名)の長さの平均。単位は文字。 |
出力 | spaghettiName | スパゲッティ料理の名称。 |
出力 | spaghettiImage | 名称に対応したスパゲッティ料理の画像のファイル名。絶対パス。 |
インデントのずれ具合から、スパゲッティのゆで時間のばらつきを算出するメトリクスプラグイン。中核となる実装言語はRubyである。
種別 | ラベル名 | 説明 |
---|---|---|
入力 | input | 一行あたりのインデントずれ具合。単位は文字。 |
出力 | output | ゆで時間のばらつき。単位は分。 |
ここで「ゆで時間のばらつき」とは、「最も早くゆでた麺」と「最も遅くゆでた麺」との時間差を言う。
プログラム中のコメント記述量から、乾燥防止としてのオリーブオイル量を算出するメトリクスプラグイン。中核となる実装言語はJavaである。
種別 | ラベル名 | 説明 |
---|---|---|
入力 | input | コメントの記述量。単位はパーセント。 |
出力 | output | オリーブオイル量。「少量」「適量」「大量」などと出力する。 |
スパゲッティの各指標値から、スパゲッティのレシピを出力するメトリクスプラグイン。中核となる実装言語はPHPである。
種別 | ラベル名 | 説明 |
---|---|---|
入力 | waitTime | ゆで上げ後の待ち時間。単位は分。 |
入力 | spaghettiName | スパゲッティ料理の名称。 |
入力 | variability | ゆで時間のばらつき。単位は分。 |
入力 | oliveOil | オリーブオイル量。 |
入力 | imageFile | スパゲッティ料理の画像ファイル名。 |
出力 | output | スパゲッティのレシピを表現するHTML。 |
なお、第6期開発は、2012年12月21日から翌年2013年1月6日まで実施した。
プログラムの指標値からスパゲッティの指標値を算出するメトリクスプラグインとして四種類を実装したが、まだ「入力値をどのような出力値に対応づけるか」の調整が残っている。 ここまで取り組んできた調査結果に基づき何かしらの根拠を添えて、その調整を行いたい。ただし、この後に実施する予備実験の結果によっては、その写像具合を再度調整する可能性がある。
また、実際に「良いプログラム」や「悪いプログラム」があれば対応付けの議論も楽になると考え、例題プログラムを五種類ほど用意した。(cf. 付録資料[4]) さらに、この対応付けを行うにあたり同輩に頼んで、彼らが数年前に拵えたというプログラムを提供してもらった。そのご厚意に心から感謝する。
用意した例題プログラムの中に、たった一つの関数のみで実装したものがある。このプログラムの「関数あたりの行数」は「54」であった。 このことを根拠に、その値が50を超えるプログラムに対して、0より大きい作り置き時間を与えることにした。 50を超える行数が多くなるほど作り置き時間を長くしたいのだが、その数値を定めようにも根拠がない。 そこで、まずは仮の数値を設定し、後の予備実験の結果を鑑みて調整し直すことにしたのだが、その結果、図3-19のようなグラフを得た。 ちなみに、1440分は24時間(1日)を意味する。ゆで上げ後の待ち時間に関する調理実験では、最長24時間のスパゲッティを食したこともあり、それに由来して1440分とした。
これは、変数名の具体例を挙げながら対応付けを決めてゆく。
変数名が3字未満の場合、例えば「v」なら、何を表しているかなど一目では分からないため、ソースの種類も見当がつかない「スパA」という名称に対応付けた。
次に3字以上5字未満の場合、例えば「val」なら、かろうじて「値(value)」と解釈できるかもしれないが、本当に「値」の意味だろうかという不安と、どのような「値」なのかという疑問を与えるものであろう。
本当に「スパゲッティ」なのかという不安や、「トマト」はソースなのか具なのかという疑問を与える「トマトスパ」に対応付けた。
同様に5字以上10字未満の場合、例えば「value」や「maxValue」なら、それが「値」であることが確実であり、また補足的な意味も表現できる。
そこで、簡潔・確実にその意味を伝えられる「トマトソースのスパゲッティ」に対応付けた。
10字以上20字未満の場合、例えば「maxValueOnList」なら、どのような値なのかという条件を的確に伝えられるが、やや冗長な印象もある。
セールスポイントを積極的に表現する「有機栽培トマトのこだわりスパゲッティ」に対応付けた。
最後に20字以上の場合、例えば「maxValueOnNewPriceList」なら、細かなニュアンスまで把握できるが、目で全体を追わねば理解に至らない。
雰囲気の演出に拘るあまり読みづらい(注文しづらい)名称「賀茂茄子と香草のスパゲッティ アンデス風 サルサ・ディ・ポモドーロを添えて」に対応づける。
# | スパゲッティ料理の名称 |
---|---|
#1 | ??? |
#2 | スパA |
#3 | トマトスパ |
#4 | トマトソースのスパゲッティ |
#5 | 有機栽培トマトのこだわりスパゲッティ |
#6 | 賀茂茄子と香草のスパゲッティ アンデス風 サルサ・ディ・ポモドーロを添えて |
一行あたりのインデントのずれ具合は最小値「0」が良い値である。そしてゆで時間のばらつき(の幅)も最小値「0」が良い値である。 つまり両者共に0を原点として比例の関係にあると考える。そこで、比例定数を用いて次のように算出することにした。
ゆで時間のばらつき[分] = 比例定数 × インデントのずれ具合[文字]
関数あたりの行数や識別子の名称の長さなどは、常軌を逸しない限り、つまりある程度は自由に決めることができる。 しかし、インデントはそうでなく、およそ一意に定まるものであり、少しのばらつきも許容されるべきではないものと考える。 例えば、10行の中で1字分ずれていた場合(ずれ具合の値が0.1だった場合)は、やや気にかかる程度として、ゆで時間のばらつきを1分とする。 つまり、比例定数を「10」に設定するのである。
アル・デンテを作り出そうとすると、ゆで時間1分のずれは死活問題となる。 同じように良いプログラムを書こうとすると、10行に1字のずれも許されないということになる。 少し厳しい気もするが、インデントに対する意識が欠落している学生が多いことから、その戒めとしてこの値を設定する。
10%未満の割り合いであれば、無視できる程度のコメントであるとして、オリーブオイルは「なし」とする。 10%以上20%未満なら、たまに目に入れる程度(香り付け程度)のコメントであるとして「少量」とする。 20%以上50%未満なら、少なくもなく多くもなくで「適量」とする。 50%以上80%未満なら、プログラムがコメントに埋もれて、その読解に支障を来たしかねないため、適量を超えて「大量」とする。 80%以上付されていようものなら、もはやそれはプログラムを読むのではなく、コメントを読んでいるようなものである。つまりオリーブオイル(コメント)が主体になりかねない状態であり「オイル漬け」とする。
なお、第7期開発は、2013年1月7日から同月8日まで実施した。
第4章 実験でも述べるが、予備実験実施後の検討で、再調整を要すると判断したメトリクスプラグインがある。以下に説明する。
作り置き時間による食感の変化が当初の予定よりも小さく、良し悪しが分かりづらいという結果が得られた。 これにより、さらに作り置き時間を長く設定することにして、先のグラフを図3-23のように変更した。 本実験では、このグラフの通りの写像を行うことにする。
なお、第8期開発は、予備実験と同日である2013年1月15日に実施した。
現段階における成果について述べる。 当初の計画で定めた期間を超過してしまったが、写像役として提起した「メトリクスプラグイン」と、メトリクスグラフを表現するための「SynapseScript」を、それぞれ開発することができた。 これらを用いたメトリクスの実例をご覧に入れたい。
身近な写像例として除算(割り算)を挙げ、具体的に「100 ÷ 3」の商を算出して示そう。
/*** Assignments ***/ x = Value("100"); y = Value("3"); division = Metrics("./path/to/Plugins/Division2"); transcript = Transcript(); /*** Synapses ***/ x to: division.dividend; y to: division.divisor; division.quotient to: transcript;
商が得られるのは、トランスクリプト(図3-25の中央に位置するウィンドウ)への出力に「division.quotient」を充てているからだ。 その部分を「division.floorQuotient」とすれば整数化された商が得られ、また「division.remainder」とすれば剰余を得ることもできる。
ちなみに「Division2」という名称は、Base64エンコードを必要としなかった頃のプラグイン「Division」に対するものである。
本研究の第一義である、プログラムからスパゲッティへの「可食化」を実現する様子を示そう。
/*** Assignments ***/ sourceFile = File("./path/to/Programs/source.c", "read"); htmlFile = File("/fullpath/to/CodeSpaghetti.html", "write"); imageFilename = Value("/fullpath/to/CodeSpaghetti.jpg"); fileCopy = Metrics("./path/to/Plugins/FileCopy"); easyMetrics = Metrics("./path/to/Plugins/C_EasyMetrics"); division = Metrics("./path/to/Plugins/Division2"); lpf2WaitTime = Metrics("./path/to/Plugins/LinesPerFunction2WaitTime"); id2Spaghetti = Metrics("./path/to/Plugins/IdLength2SpaghettiName"); indent2Variability = Metrics("./path/to/Plugins/IndentDifference2VariabilityOfBoilingTime"); comment2OliveOil = Metrics("./path/to/Plugins/CommentRatio2OliveOil"); codeSpaghetti = Metrics("./path/to/Plugins/CodeSpaghetti"); timeUnit = Value("minutes"); number2Time_1 = Metrics("./path/to/Plugins/Number2Time"); number2Time_2 = Metrics("./path/to/Plugins/Number2Time"); /*** Synapses ***/ sourceFile to: easyMetrics; easyMetrics send: effectiveLOC to: division.dividend send: quotient to: lpf2WaitTime to: number2Time_1.value to: codeSpaghetti.waitTime; easyMetrics send: numberOfFunctions to: division.divisor; easyMetrics send: averageLengthOfId to: id2Spaghetti send: spaghettiName to: codeSpaghetti.spaghettiName; easyMetrics send: indentDifference to: indent2Variability to: number2Time_2.value to: codeSpaghetti.variability; easyMetrics send: commentRatio to: comment2OliveOil to: codeSpaghetti.oliveOil; id2Spaghetti send: spaghettiImage to: fileCopy.source; imageFilename to: fileCopy.destination; imageFilename to: codeSpaghetti.imageFile; timeUnit to: number2Time_1.unit; timeUnit to: number2Time_2.unit; codeSpaghetti to: htmlFile;
第6期開発で定めたグラフ(図3-15)とは異なることに注意されたい。 当時のグラフ構造を実装する予定だったが、画像データをBase64エンコードに掛けて受け渡しを行うと、コマンド文字列が長すぎるという理由(シェルの制約)により実行できなかったのだ。 そこで、データそのものを受け渡すのではなく、そのデータの在り処(つまり画像ファイルへのパス)を用いて間接的に参照し、プラグイン「FileCopy」によるコピー操作で実現することができた。
同じく、図3-15に存在しなかったプラグイン「Number2Time」を新たに用いているが、これは「待ち時間」や「ゆで時間のばらつき」の出力値を読みやすい形式に変換するためのものである。 これら「FileCopy」や「Number2Time」の詳細については、それぞれのヘルプ機能(make help)をご覧頂きたい。
従来のメトリクスツール(得られた指標値を一覧表示するもの)に似せた例を示そう。なお、やや煩雑になることからメトリクスグラフは割愛する。
/*** Assignments ***/ sourceFile = File("./path/to/Programs/source.c", "read"); htmlFile = File("/fullpath/to/MetricsTable.html", "write"); easyMetrics = Metrics("./path/to/Plugins/C_EasyMetrics"); division = Metrics("./path/to/Plugins/Division2"); lpf2WaitTime = Metrics("./path/to/Plugins/LinesPerFunction2WaitTime"); id2Spaghetti = Metrics("./path/to/Plugins/IdLength2SpaghettiName"); indent2Variability = Metrics("./path/to/Plugins/IndentDifference2VariabilityOfBoilingTime"); comment2Degradation = Metrics("./path/to/Plugins/CommentRatio2Degradation"); comment2OliveOil = Metrics("./path/to/Plugins/CommentRatio2OliveOil"); metricsTable = Metrics("./path/to/Plugins/MetricsTable"); timeUnit = Value("minutes"); number2Time_1 = Metrics("./path/to/Plugins/Number2Time"); number2Time_2 = Metrics("./path/to/Plugins/Number2Time"); /*** Synapses ***/ sourceFile to: easyMetrics; easyMetrics.linesOfCode to: metricsTable.linesOfCode; easyMetrics.effectiveLOC to: metricsTable.effectiveLOC; easyMetrics.physicalLOC to: metricsTable.physicalLOC; easyMetrics.logicalLOC to: metricsTable.logicalLOC; easyMetrics.commentLines to: metricsTable.commentLines; easyMetrics.numberOfFunctions to: metricsTable.numberOfFunctions; easyMetrics.averageLengthOfId to: metricsTable.averageLengthOfId; easyMetrics.indentDifference to: metricsTable.indentDifference; easyMetrics.standardIndent to: metricsTable.standardIndent; easyMetrics.commentRatio to: metricsTable.commentRatio; easyMetrics send: effectiveLOC to: division.dividend send: quotient to: lpf2WaitTime to: number2Time_1.value to: metricsTable.waitTime; easyMetrics send: numberOfFunctions to: division.divisor; division send: quotient to: metricsTable.linesPerFunction; easyMetrics send: averageLengthOfId to: id2Spaghetti send: spaghettiName to: metricsTable.spaghettiName; easyMetrics send: indentDifference to: indent2Variability to: number2Time_2.value to: metricsTable.variability; easyMetrics send: commentRatio to: comment2Degradation to: metricsTable.degradation; easyMetrics send: commentRatio to: comment2OliveOil to: metricsTable.oliveOil; timeUnit to: number2Time_1.unit; timeUnit to: number2Time_2.unit; metricsTable to: htmlFile;
最後に、少しだけ実用的な例を示したい。仕事の現場で何か見積もりを行う際、楽観的に見た時の数値・悲観的に見た時の数値・最頻値(標準値)の三つから、見積もりを行おうというものである。 ここでは、その見積もり期待値の算出を例示しよう。
見積もり期待値 =(楽観値 + 最頻値×4 + 悲観値)÷ 6
種別 | ラベル名 | 説明 |
---|---|---|
入力 | optimistic | 楽観的に見た時の数値。楽観値。 |
入力 | standard | 悲観的に見た時の数値。悲観値。 |
入力 | pessimism | 最頻値。標準値。 |
出力 | expectation | 見積もりの期待値。 |
出力 | variance | 見積もりの分散。 |
出力 | probability | 見積もりの精度 |
/*** Assignments ***/ optimisticValue = Value("10"); standardValue = Value("12"); pessimismValue = Value("20"); estimation = Metrics("./path/to/Plugins/EstimationBy3Points"); transcript = Transcript(); /*** Synapses ***/ optimisticValue to: estimation.optimistic; standardValue to: estimation.standard; pessimismValue to: estimation.pessimism; estimation send: expectation to: transcript;
以上、四種類のメトリクスが実現されたことを示した。 「プログラムからスパゲティへの可食化の実例」によって可食化が実現できたことを示し、 その他の数例を示すことによって再利用性の高いツールが実現できたことを示せたであろう。
なお、このメトリクスツール(及び上記四題のメトリクス)を利用するには、以下の構成要素が不可欠となる。参考として、開発時に利用していたバージョンや条件を括弧内に付しておく。
現段階のメトリクスツールが持つ問題点と今後の対処について述べる。
メトリクスグラフの評価(写像処理の実行)は、写像元からのデータ供給に始まり、メトリクスプラグインによる写像を経て、写像先まで到達する。 このうちの先頭である「写像元からのデータ供給」について、スパゲッティへの可食化の例ように複数の写像元が存在し得るのだが、その場合、そのどちらも並行にデータ供給を開始することができるはずである。 しかし、開発したSynapseScriptの処理系は、その評価を全て「逐次処理」で実装しており、より効率のよい「分散処理」で実現することが望まれる。 ただし、その実現には、分散システムの学域で議論される諸問題(プロセス間の干渉など)への対処を新たに追加する必要がある。
もし、メトリクスグラフで循環構造(再帰構造)を表現することができれば、フィボナッチ数の算出やニュートン法など、再帰的な構造を持つ計算処理を実現することもできよう。
しかし、問題点が存在する。再帰構造に入ったところ(図3-31のノードB)で、必要な入力値二つを得られないため、その先の評価が恒久に行なわれないのである。 プログラミングにおける再帰構造では、再帰構造に入った後に、その中で記述されている再帰構造に遷移するのだが、 メトリクスグラフの場合は、再帰構造に入る前に、中の再帰構造の評価結果を要するのである。つまり、再帰を表現することにメトリクスグラフは向いていないものと考えられる。 この問題点をツール側の工夫で解決することはできないものか、今後の検討で打開案を見出すことが期待される。
開発したメトリクスツールの検証を目的に、以下に挙げる環境で動作確認を実施した。
2013年2月2日、下記マシンにてメトリクスツールの正常動作を確認した。また、このマシンを利用してツールの開発を行なったということも記しておく。
|
2013年2月2日、下記マシンにてメトリクスツールの正常動作を確認した。
|
2013年2月2日、下記マシンにてメトリクスツールの正常動作を確認した。
|