2

コマンド ライン ツールとして使用するプログラムがある場合、デバッグのオプションは何ですか?

例として、プログラムが次のようになっているとします。

のリストdo_stuff.pl:

main :-
    current_prolog_flag(argv, Argv),
    do_stuff(Argv),
    halt.
main :-
    halt(1).

SWI-Prolog を使用すると、次のようにコンパイルできます。

swipl --goal=main -o do_stuff -c do_stuff.pl

そして、単に呼び出すだけで実行できます

$ ./do_stuff foo bar baz

現状では、失敗した場合、これは 1 で終了しdo_stuff/1ます。失敗した最初の (最も早く、最も深い) ゴールを確認するにはどうすればよいですか? またはさらに良いことに、バックトレース全体ですか?たとえば、次のようにdebugandを使用できるはずだと思いました。leash

main :-
    current_prolog_flag(argv, Argv),
    debug, leash(+fail),
    do_stuff(Argv),
    halt.

...しかし、私が試したものは何もありませんでした。

私が持っていた唯一の中途半端なアイデアは、決定論的に成功すると予想しているが成功しないすべての述語に対してエラーをスローすることでした。これはもちろん実行可能ですが、少し過剰に思えますか?

動機

コマンド ライン ツールとして使用されるプログラムは、(通常) 1 回実行し、引数を取り、入力を読み取り、出力を書き込むことを意図しています。この場合、失敗とは何を意味するのでしょうか。私の解釈では、予期しない失敗はプログラムのエラーです。

単体テストが役立つ場合があります (述語を分離してテストします)。ただし、これは定義上、プログラマーが問題、スコープ、またはツールを理解していないために発生するエラーには役立ちません。現実的な入力でプログラムを実行するだけで、このクラスのエラーが検出されます。

上記の例で、特定のユースケースが原因do_stuff/1で失敗し、プログラムがゼロ以外のコードで終了した場合、プログラマーはどの述語が失敗したかを判断するためにどのような選択肢があるでしょうか?

コメントにリンクされている回答は、 1つの解決策を提供します。しかし、(私が正しく理解していれば) これには、問題の述語呼び出しが見つかるまで、プログラマーが実行フローに沿って体系的にチェックする必要があります。

これはまさに私が避けたかったことです。

4

1 に答える 1

3

よりコマンド指向の言語と比較して、Prolog では失敗は非常に珍しいことです。実際、Prolog 0 (Prolog I の前のバージョン) でさえ、トレース オプションの横に、失敗のみを表示ECRIREする特別なオプションがありました。IMPASSES

その後、失敗がどのように説明されるかを自動的に理解しようとする、特にミレル・デュカッセによる研究があります。

失敗の奇妙な点は、必ずしも何かがうまくいかなかったことを示しているわけではないということです。しかし、時にはそうです。

失敗を理解する方法は 2 つあります。前者はより手続き的で、後者はより宣言的です。

注釈

多くのプログラムで(@)/1、目標が常に成功することを期待していることを示すために使用します。演算子宣言のおかげで、これは 1 つの余分な文字です。

   ...,
   @goal_aux_togoalaux_spec(OQuery, FVect0, Query, Spec),
   ...

失敗したゴールの場合、エラーが発行されます。ネストされた例外も文書化することも重要です。タイム クリティカルなものがある場合は、これら@を削除する必要があります。ただし、120kLOP で約 400 を数えただけです。

@いくつかの答えがある目標に対してもうまく機能することに注意してください。のように @member(1,[X,Y])

この手法は、デファクトモードのプログラムに適しています。の準備を考えてみてください(上記の例です)。ここで、主に考えている状況にいます。これがプログラムです。適切なスライスとは何ですか? そのような状況では、「いいえ、スライスはありません」という答えは答えになりません。あなたはそれが常に成功することを本当に期待しています。そのようなモードプログラムを持っていない場合は、多くの場合、既存のモード化されていないプログラムを不変にすることで変換できます:

p(X, Y) :-
   wellformed(X),
   @p_old(X, Yc),
   Yc = Y.

この手法は、純粋にリレーショナルな宣言型コードでは急速に魅力を失います。最近の例を見てみましょう。そこに、最初の目標を除いて - を追加することは事実上不可能です。このような状況では、次のようなより宣言的なアプローチが必要です。@

一般化

より複雑な問題の場合、@はうまく機能しません。代わりに、プログラムの変更/スライスが必要です。プレフィックスを追加してプログラムを一般化する必要があります*。この手法を手動で使用する SO のデバッグ セッションのコレクションについては、この回答を参照してください。この手法の主なポイントは、最大の一般化を決定する際に、プログラムの本当の意味を理解する必要がないことです。失敗した目標を監視する必要があるだけです。

理想的には、そのような一般化は自動的に生成されます。しかし、多くの障害があります。一つには、それらは純粋な単調コードに対してのみ機能します (実際、これは、そのようなコードに固執する理由の 1 つです)。したがって、最初に既存のコードを分析して分類する必要があります。システムが適合せず、ランダムに動作を変更する場合(あなたが言及したシステムのように)、これはさらに困難です。

于 2016-04-16T19:23:00.883 に答える