AZ-Prologにはプログラム開発を効率的に行うためのユーティリティ・プログラムが付属しています。 ここでは、これらの使い方を説明します。
Prologソースプログラムのラインカウンタです。本プログラムはAZ-Prologパッケージの中に、実行形式(スタンドアローンアプリケーション)として、含まれています。
C:¥>count -p ファイル名並び↓
と入力することにより、各ファイルのライン数、節の数、述語数が表示され、最後に全ファイルのライン、節、述語の合計が表示されます。出力をファイルに落としたければ、リダイレクトを使って次のようにしてください。
C:¥>count -p queen.pl setof.pl>outfile↓
Prologの場合、1行中にゴールを並べて書くことがあり、またコメントや空白行を入れて、見易くすることがありますので、エディタ上のライン数がそのままプログラムの大きさにはなりません。
本ユーティリティでは、プログラムだけを扱いますので、ソースの管理に役立つと思います。尚、ライン数のカウントは、頭部は1ライン、ボディ部の各ゴールも各1ラインとし、ORゴール、callゴールなど、ゴールを引数とするゴールは更にそのゴール数を加えています。
その他の用途として、述語に含まれる節数が、どうであるか、また一節に平均して何ライン含まれるかなどの調査にも応用できます。
AZ-Prologパッケージの中のスタンドアロンアプリケーションで、Prologソースのリントユーティリティ(プログラム・チェッカー)です。
C:¥>azlint -p ファイル名並び {/log} ↓
/logを付けると、画面と同時に、azlint.logというファイルに診断内容を出力します。
各ファイル、1モジュール毎に次のような情報を得ることができます。
(1)シンタックスエラーのチェック
(2)決定性にコンパイルされる述語の一覧
(3)非決定性にコンパイルされる述語の一覧
(4)インタプリティブコールとなるゴールの一覧
(5)使用している組込述語の一覧
(6)extern宣言群のうち、使用しているものの一覧
(7)組込述語のオーバーライト定義のワーニング
(8)public宣言をしている述語が1つもないときそのワーニング
(9)無視するpublic宣言、無視するコマンド
(10)publicでない述語でモジュール中でも使われていない述語の一覧
ただし、これらの情報は「コンパイルされた場合にどうなるか」ですので、インタプリタコード上では非決定であってもコンパイル時には決定性述語という場合もあります。(主にモード宣言に関して発生する点です。ただしインタプリタ上でも厳密にモード宣言どうりに使われていれば、効率上はともかく同じ結果にはなります。)
各情報について順番に説明しながら、azlintを使ったデバッグについても言及しましょう。
ファイルにシン夕ックスエラーがあると、その節位置番号(つまりファイル中の何番目に書かれている節か、であって述語の何節めかではありません)とエラーとなった周辺の内容を表示して処理を停止します。
シンタックスエラーは、単純な括弧の対応不良、ピリオドの付け忘れといったレベルからオペレータの定義内容に係わる複雑なものまで多岐にわたりますから、エラー箇所の前後を注意して良く見る必要があります。オペレータやオペレータと同じアトムを使っている場合は、まずオペレータ定義の内容を調べて下さい。大抵は括弧を補ったり、正規の表現にすることで何とかなります。
test(X):-X= = spy → true. = = , spy, → がオペレータ定義 ↓ test(X):-X = =(spy)→ true.
シンタックスエラーは、インタプリタ上で、“echo(_,on).”としてからコンサル卜するか、AzEdit上でコンサル卜することでも判りますが、AZLINTが最も簡便です。
決定性に書ける処理(述語)は決定性にすべきです。メモリ効率処理効率が違って来るからです。決定性と判定された述語は、他モジュールで使う(他モジュールでextern宣言する)ときにはその情報を付加しておきましょう。(detプレフィックスを付ける)
例:: extern det :述語名/アリティ.
決定性にするには、カットオペレータ、モード宣言を積極的に使うことのほかに、externの述語を使うときにその述語が決定性であるかどうかも重要です。
例:決定性とならない 決定性となる append([],L,L). append([],L,L):-!. append([AIL],B,[AIC]):- append([AIL],B, [AIC]):- append (L,B, C). append(L,B,C). または左の定義のままで :-mode append(+,+,-).を追加する。
決定性になるように記述したつもりでも、非決定性と判定されたときは要注意です。本来のロジック上の誤りである場合以外に、バグが潜んでいる可能性があります。
a) | externの述語を使っており、これが決定性なのにdet:宣言がない、またはextern宣言そのものを忘れ、イシタプリティブcall(組込が非決定述語 |
b) | ゴールの一部にスペルミスした述語callがあり、インタプリティブcallとなっている。 |
c) | モード宣言の引数ミス。 |
d) | ピリオドとカンマをまちがえて次の単位節を最終ゴールと見なしている。 (意外に多いミスです。) |
a:-b,c,←本来はピリオド。 b.
本来、動的にassertまたはconsultして使われる述語以外のものが表示された場合は,前項の中のa)、b)であることは間違いありません。
一部の組込述語(主に画面制御用)を使っているプログラムは、コンパイルオプションとして“/curses”または“/i”が必要です。 使われている組込述語の一覧を見ることでチェックできます。
インタプリタでは、組込述語と同じファンクタ、アリティを持つ述語のコンサルトはエラーとなりますが、コンパイルすることはできます。 すなわち、低レベルの組込述語を組み上げて上位の組込述語の動作を定義しなおす(putを使ってwriteを最定義、getを使ってreadを最定義する等)ことなどができます。ただし、こうした意図でないのにこのこのワーニングが出たときは、明らかにソースファイル中のミスです。
例:a:-b, | |||||
c. | ← | ピリオドとカンマの書きまちがい。 |
|||
write(a). |
|
||||
↑ | |||||
write/1の定義になってしまう。 |
AZ-Prologでは、外部モジュールから呼ぶことの出来る述語は、publicの宣言をするようになっています。
:-public 述語名/アリティ.
:-publicall.(全述語)
とすることで、他モジュールから呼ぶこともできますし、インタプリタからも使うことができます。(組込述語として登録される)。
もう1つ書き方があり、
:-public invisible :述語名/アリティイ.
とすると、組込述語の登録は行いません。すなわち、他モジュールで使うことはできますが、必ずそのモジュールではextern宣言を入れなければなりません。
publicでない述語は、モジュール内のみにおいて使うことが出来ます。言い換えると、他の述語のサブ述語となります。
こうした述語をstatic述語と呼びます。static述語は他から見えませんので、同じ述語名、アリティのものが他モジュールにあってもリンクエラーを起こすことがありません。
ただし、同一モジュール中でまったく使われていないstatic述語は外からも呼ばれないので、結局はムダとなります。開発中では良く起きることではありますが、完成したアプリケーションをコンパイルするときには、ファイルから削除するか、コメントにしておきましょう。
多くの節からなる述語で引数の数のミスをした節が混じっていると、当然のことながら別述語となってしまいます。一覧表を見て同一述語名で引数違いが含まれていれば、意図したものか見なおしましょう。
前記の説明で十分でしょう。必要に応じて役立てて下さい。
AZ-Prologコンパイラでは、モジュール単位にオブジェクトになり、ファイル中には任意にモジュール宣言を入れることになっています。この時、自己モジュールになく、他モジュール中で定義されている述語を使うときは、extern宣言を入れた方が、直接関数呼び出しとなり、高速化されます。 しかし、実際問題として開発中に複数モジュール中の定義を常にチェックしながら宣言を書いていくのも面倒です。 こんな場合には、azlintでlogをとり、そのログファイルをエディタに読み込んで、interpritive callとして出力されている述語名一覧を切り出して、extern宣言に一括変換したものを元のファイルに追加すれば簡単に出来ます。
Prologによくあるバグで、Lintには反映されないものとして変数名のミススペルがあります。Prologは変数宣言の必要も型指定も不要で、また未束縛変数であっても引数として許されるので、他言語のように、コンパイル時やリントユーティリティではチェックしようがなく、実行時に組込述語でエラーが発生してわかる場合があります。
ANS=3,X is Ans+4, | …… | 〈実行時エラー〉 |
〈別変数となるケース〉 |
次のようなケースではケースをよくながめる以外ないでしょう。
定義 | a(l,one) | 実行 | ?-SP=2,a(Sp,X). | |
a(2,two) |
述語情報の中に、頭に数字の付いた、定義したおぼえのない述語が含まれていることがありますが、これはLint自身がソースプログラムを読み込む時に、解析しながら、展開/たたみ込みをして生成したものです。
主に節中にOR述語、IFThen、ゴール列を引数とした述語(call, errorsetなど)などの切り出し部分です。
azlint は、AZ-Prologコンパイラのソースー次解析部と同一ルーチンを使って記述されています。