この章では、AZ-Prologインタプリタに組み込まれているデバッガの使い方について説明します。このデバッガは虫取りだけでなく、インタプリタの動作そのものの理解の助けにもなると思います。
まず、次項の「6-2.デバッガの基本的な使い方(トレースとスパイ)」においてデバッガの基本的な使い方を解説します。
AZ-Prologのトレースは、インタプリタが今現在何をしているのかの情報をすべてターミナルに出力します。表示される情報量は多く、どのような局面で情報を表示するかは設定で変えられるようになっています。情報量を選択することも可能です。
なにはともあれ、早速使ってみましょう。
その前に、
| 理解する(松尾さん,ワビ). /*松尾さんは“ワビ”がわかる。*/ | 理解する(松尾さん,サビ). /*松尾さんは“サビ”がわかる。*/ | 理解する(ブリキ屋さん,サビ). /*ブリキ屋さんは“サビ”がわかる。*/ | 理解する(X,風流):- 理解する(X,ワビ). /*“ワビ”がわかる人は風流を理解する。*/ | 理解する(X,風流):- 理解する(X,サビ). /*“サビ”がわかる人は風流を理解する。*/
というプログラムを何等かの方法で入力してください。このサンプルは他の章でも説明に多用しているプログラムです。
邪魔なコードが混じっていない5-4-1.の例の部分を利用する方法もあります。正しく入力出来たか確かめてみます。
| ?-listing. 理解する(松尾さん,ワビ). 理解する(松尾さん,サビ). 理解する(ブリキ屋さん,サビ). 理解する(X,風流) :- 理解する(X,ワビ). 理解する(X,風流) :- 理解する(X,サビ). yes | ?-
まず、トレースを使ってみます。
| ?-trace. yes debug mode on ||?-
これで、次の質問からトレースがかかります。 厳密には「trace/0」が成功した時点からトレースがかかっています。プロンプトが「| ?-」から「||?-」に変わったのは、インタプリタがデバッグモード( 「6-3.デバッグモード」参照)で動いているということを知らせるためです。(トレースまたはスパイをかけると自動的にデバッグモードになります。 ) また、「LOOP=」に続く整数値はインタプリタがゴールを実行する際に述語を呼出した回数(下のトレースの実行例で、Tryした回数と言えば分かり易いですね。)です( 「6-4-3.ループカウンタ」参照)。
||?-理解する(ブリキ屋さん,風流). [1] 0 Try : 理解する(ブリキ屋さん,風流) ? (1) Match : 理解する(ブリキ屋さん,風流) :- 理解する(ブリキ屋さん,ワビ). (2) [2] 1 Try : 理解する(ブリキ屋さん,ワビ) ? (3) [2] 1 Fail : 理解する(ブリキ屋さん,ワビ) (4) [1] 0 Pop : 理解する(ブリキ屋さん,風流) (5) [1] 0 Retry : 理解する(ブリキ屋さん,風流) ? (6) Match : 理解する(ブリキ屋さん,風流) :- 理解する(ブリキ屋さん,サビ). (7) << LAST CALL >> (8) [1] 0 Try : 理解する(ブリキ屋さん,サビ) ? (9) Match : 理解する(ブリキ屋さん,サビ). (10) [1] 0 Succ : 理解する(ブリキ屋さん,サビ) (11) yes LOOP = 3 ||?-
これらは以下のようなことを意味します。
(1)これから「理解する(ブリキ屋さん,風流)」を実行します(Try)。 (2)「理解する(ブリキ屋さん,風流)」と単一化可能な頭部を持つ節がみつかりました。それはこの節です(Match)。 (質問: 「理解する(ブリキ屋さん,風流)」を「理解する(ブリキ屋さん,ワビ)」に置き換えます。) (3)これから「理解する(ブリキ屋さん,ワビ)」を実行します(Try)。 (4)「理解する(ブリキ屋さん,ワビ)」と単一化可能な頭部を持つ節はありません(Fail)。 (5)「理解する(ブリキ屋さん,風流)」を実行しようとして、途中までうまくいきましたがだめでした。内部状態を元に戻します(Pop)。 (6)「理解する(ブリキ屋さん,風流)」 の別解を探します(Retry)。 (7)「理解する(ブリキ屋さん,風流)」に別の選択肢がありました。(Match)。 (質問: 「理解する(ブリキ屋さん,風流)」を「理解する(ブリキ屋さん,サビ)」に置き換えます。) (8)この節は定義の最終節です( << LAST CALL >>)。 (9)「理解する(ブリキ屋さん,サビ)」を実行します(Try)。 (10)「理解する(ブリキ屋さん,サビ)」と単一化可能な頭部を持つ節がみつかりました。それはこの節です(Match)。 (11)「理解する(ブリキ屋さん,サビ)」の実行に成功しました(Succ)
実行例の様に、デバッガの出力するメッセージには「Try」「Match」「Succ」「Fail」「Pop」「Retry」という、実行動作の局面を表す文字列が含まれています。
それぞれがどのような場合に出力されるのか、以上の説明によっておおよそ理解してもらえたと思います。
(詳しくは「6-4.デバッガから出力される情報」を参照してください。)
トレースを終えるには次のようにします。
||?-notrace. [1] 0 Try : notrace ? << BUILTIN CALL >> yes LOOP = 1 ||?-
「《BUILTIN CALL》」というのは「Try」した「notrace」が組込述語であった事を表しています。
トレース終了後のプロンプトを見て気付かれたと思いますが、デバッグモードは解除されていません。これを解除するには次のようにします。
||?-nodebug. yes debug mode off | ?-
トレースは上の例でも分るように、ゴール(質問)の実行の最初から最後までに呼び出された全述語の実行の過程・結果を表示します。
従って、上の例ように簡単なプログラムの場合はともかく、ちょっと大きなプログラムになると出力される情報は膨大な量になってしまいます。
その例として次のサンプルプログラムを試してみましょう。このファイルはAZ-Prologのインストールディレクトリ下の「sampleother」に入っています(Linux版ではshare/azprolog/sample/other)。
| ?-[-'queen.pl'].
これはよく知られた8クイーンを解くプログラムです。
ただし、このプログラムは8クイーンだけではなくnクイーン(即ち、互いに取り合えないようなn×nの盤上のn個のクイーンの配置を求める問題です。)を解くことができます。
| ?-queen(4,X). X = [2,4,1,3] yes | ?-
試しに、これにトレースをかけてみてください。
| ?-trace. yes debug mode on ||?-queen(4,X). [1] 0 Try : queen(4,X_5) ? Match : queen(4,X_5) :- generate(4,L1_11), put(L1_11,[],X_5). [2] 1 Try : generate(4,L1_11) ? Match : generate(4,[4|L_15]) :- N1_17 is 4-1, generate(N1_17,L_15). [3] 2 Try : N1_17 is 4-1 ? << BUILTIN CALL >> [3] 2 Succ : 3 is 4-1 << LAST CALL >> [2] 1 Try : generate(3,L_15) ? Match : generate(3,[3|L_26]) :- N1_28 is 3-1, generate(N1_28,L_26). [3] 2 Try : N1_28 is 3-1 ? << BUILTIN CALL >> [3] 2 Succ : 2 is 3-1 << LAST CALL >> [2] 1 Try : generate(2,L_26) ? a <== Aコマンドを入力し(Enterキー)を押す LOOP = 6 ||?-
おそらく、うんざりしてきたと思います。
(デバッガが「……?」ときいてきたときに「a 」を入力すればインタプリタのトップレベルに戻ります。)
こんなときのためにスパイがあります。 (下図の実行例で、?の後に縦棒のように見えているのは小文字のL(エル)です。Lコマンドについては「6-5」を参照してください。)
||?-notrace. [1] 0 Try : notrace ? << BUILTIN CALL >> yes LOOP = 1 ||?-spy put/3. yes LOOP = 1 ||?-queen(4,X). [1] 0 Try : put([4,3,2,1],[],X_5) ? l <== Lコマンドを入力して改行 [3] 1 Try : put([3,2,1],[4],X_5) ? l [5] 2 Try : put([3,1],[2,4],X_5) ? l [5] 2 Pop : put([3,1],[2,4],X_5) [5] 2 Retry : put([3,1],[2,4],X_5) ? l [5] 2 Fail : put([3,1],[2,4],X_5) [5] 2 Try : put([3,2],[1,4],X_5) ? l [7] 3 Try : put([2],[3,1,4],X_5) ? l [7] 3 Pop : put([2],[3,1,4],X_5) [7] 3 Retry : put([2],[3,1,4],X_5) ? l [7] 3 Fail : put([2],[3,1,4],X_5) [5] 2 Pop : put([3,2],[1,4],X_5) [5] 2 Retry : put([3,2],[1,4],X_5) ? l [5] 2 Fail : put([3,2],[1,4],X_5) [3] 1 Pop : put([3,2,1],[4],X_5) [3] 1 Retry : put([3,2,1],[4],X_5) ? l [3] 1 Fail : put([3,2,1],[4],X_5) [3] 1 Try : put([4,2,1],[3],X_5) ? l [5] 2 Try : put([4,2],[1,3],X_5) ? l [7] 3 Try : put([2],[4,1,3],X_5) ? l [9] 4 Try : put([],[2,4,1,3],X_5) ? l [9] 4 Pop : put([],[2,4,1,3],X_5) [9] 4 Retry : put([],[2,4,1,3],X_5) ? l [9] 4 Succ : put([],[2,4,1,3],[2,4,1,3]) [7] 3 Succ : put([2],[4,1,3],[2,4,1,3]) [5] 2 Succ : put([4,2],[1,3],[2,4,1,3]) [3] 1 Succ : put([4,2,1],[3],[2,4,1,3]) [1] 0 Succ : put([4,3,2,1],[],[2,4,1,3]) X = [2,4,1,3] yes LOOP = 124 ||?-
スパイはトレースすると、大量に出力される情報を人間が指定したものだけに制限します。上の例は、述語queen/2の中で呼ばれるput/3だけに限定して、その実行の流れを辿ったものです。(スパイは、機械語プログラムのデバッグ時によく使用されるブレークポイントに相当します。)
また当然ですが、複数個の述語にスパイを同時にかけることもできます。
スパイがどの述語にかかっているかは次のようにして知ることができます。
||?-debugging. << DEBUGGING >> Debug Mode = debug (1) Unknown = fail (2) Spy Points = put/3 (3) Command Point = Try Retry (4) Display Point = Try Match Succ Fail Pop Retry (5) Verbose Mode = middle (6) yes LOOP = 1 || ?-
このように「debugging/0」によって
(1)現在デバッグモードか否か、もしくはトレースがかかっているかどうか。
(2)定義されていない述語を実行しようとしたとき、失敗するかトレースを始めるか。
(3)スパイがかかっている述語。
(4)トレース時にどの局面でコマンド入力を受け付けるか。(leash/1で設定。設定方法は「6-5-1.」を参照してください。)
(5)トレース時及びスパイにおいてどの局面を表示するか。(leash1/1で設定。設定方法は「6-4-3.(1)」を参照してください。)
(6)トレースの際に出力する情報量。( leash2/1 で設定。設定方法は「6-4-3.(2)」を参照してください。
という6つの情報を得ることができます。
スパイの外しかたには2通りあります。ひとつは以下のような方法です。
||?-nospy put/3. yes LOOP = 1 ||?-
もうひとつは「nodebug/0」によってデバッグモードを解除する方法です。
この述語は、デバッグモードを解除すると同時にすべてのスパイを外します。
||?-nodebug. yes debug mode off | ?-
以上は「述語名とアリティ」を指定したスパイポイントの設定・解除ですが、この他に「述語名のみ」「述語名/アリティ/節順位」「述語名/アリティ/節順位/ボディ部のゴール順位」を指定したスパイポイントの設定・解除もできます。
<例 「述語名/アリティ/節順位」を指定したスパイポイントの設定>
| ?-['trans.dcg']. <== trans.dcg をコンサルト yes | ?-listing(jn). jn([n,'I'],[私|_0],_0). jn([n,you],[あなた|_0],_0). jn([n,arrow],[矢|_0],_0). jn([n,flies],[蝿|_0],_0). jn([n,flies],[てんぷら|_0],_0). <== 5節目。これがTryされるところでトレース開始したい jn([n,time],[時|_0],_0). jn([n,like],[好み|_0],_0). yes | ?-spy jn/3/5. <== 5節目にSpyをかける yes debug mode on ||?-debugging. <== デバッグ情報を確認 << DEBUGGING >> Debug Mode = debug Unknown = fail Spy Points = jn/3/5 <== jn/3の5節目にSpyが設定されている Command Point = Try Retry Display Point = Try Match Succ Fail Pop Retry Verbos Mode = middle yes LOOP = 1 ||?-trans("time flies like an arrow",X,Y). X = [s,[np,[n,time],[np,[n,flies]]],[vp,[vt,like],[np,[det,a],[n,arrow]]]] , Y = [時,の,蝿,は,ひとつの,矢,を,好む]; <== ここまでトレースがかからなかったので";"で別解要求 [12] 3 Retry : jn([n,flies],_4_387,[は|_4_377]) ? Match : jn([n,flies],[てんぷら,は|_4_377],[は|_4_377]). <== 5節目のRetryでトレースが開始されている。 [12] 3 Succ : jn([n,flies],[てんぷら,は|_4_377],[は|_4_377]) [10] 2 Succ : jnp([np,[n,time],[np,[n,flies]]],[時,の,てんぷら,は|_4_377],[は| _4_377]) [13] 2 Try : jvp([vp,[vt,like],[np,[det,a],[n,arrow]]],_4_377,[]) ? l [13] 2 Try : jvp([vp,[vt,like],[np,[det,a],[n,arrow]]],_4_377,[]) ? l X = [s,[np,[n,time],[np,[n,flies]]],[vp,[vt,like],[np,[det,a],[n,arrow]]]] , Y = [時,の,てんぷら,は,ひとつの,矢,を,好む] yes
<例 「述語名/アリティ/節順位/ボディ部のゴール順位」を指定したスパイポイントの設定>
| ?-listing. a. a(1) :- true, write(a1), nl. a(2) :- true, write(a2), nl. <== a/1第2節の第2ゴール、write(a2)がTryされるところでトレースを開始したい a(3) :- true, write(a3), nl. yes | ?-spy a/1/2/2. yes debug mode on ||?-debugging. << DEBUGGING >> Debug Mode = debug Unknown = fail Spy Points = a/1/2/2 Command Point = Try Retry Display Point = Try Match Succ Fail Pop Retry Verbose Mode = middle yes LOOP = 2 ||?-a(X),fail. a1 [2] 1 Try : write(a2) ? p <== Pコマンドで呼び出し祖先を確認 1: write(a2) 0: a(2) :- true, *> write(a2), <== トレース開始点が a/1/2/2であることが確認できる nl. Goal: ?- a(2),fail.
・ノーマルモードの場合
「| ?-」(ユーザモード)あるいは「! ?-」(システムモード)
・デバッグモードの場合
「||?-」(ユーザモード)あるいは「!!?-」(システムモード)
このデバッガでは、インタプリ夕の基本的な動作の局面を「Try、 Match、Succ、Fail、Pop、Retry」の6つに分けています。ですので、まず「6-4-1.インタプリタの基本的な動作局面」で各局面について説明します。その上で、「6-4-2.局面ごとの出力情報」では、どの局面で何が出力され、それはどのような意味を表しているのかを説明します。「6-4-3.出力情報の制御」では、デバッガが情報を出力する局面を変更したり、情報量を変える方法を説明します。デバッガの出力中に含まれる「LOOP =」について理解して頂くため、ループカウンタについて「6-4-4.ループカウンタ」で解説しています。また、「6-4-5.述語コールカウンタ」では、プログラム実行中にどの述語が何回呼ばれたかの集計情報を表示し、バグの検出や実行効率の向上に役立てる機能について説明しています。
(1)Try |
|
||||||
---|---|---|---|---|---|---|---|
(2)Match |
|
||||||
(3)Succ |
|
||||||
(4)Fail |
|
||||||
(5)Pop |
|
||||||
(6)Retry |
|
トレースをかけると、インタプリタが6つの局面の内のどれかに達した時、その局面名と現在インタプリタが注目しているゴールまたは節を表示します。これによって、今現在インタプリタが何をしているかがわかります。
ただし、このとき表示されるゴールまたは節は、それらの定義イメージそのままではありません。各局面に共通することなので、局面ごとの出力の説明に移る前に、このことについて若干説明します。
デバッガがゴールや節の情報を表示する場合、その中に現れる変数に値が代入(ユニファイ)されていたら、変数の部分は変数名ではなくその値が表示されます。
<例>
||?-listing. a(X) :- write(X). /*節の定義イメージ*/ yes LOOP = 1 ||?-trace,a(b(c)). /*a(b(c))をトレース実行*/ [1] 0 Succ : trace [1] 0 Try : a(b(c)) ? Match : a(b(c)) :- write(b(c)). /*節中の変数Xはb(c)に置換されている*/ << LAST CALL >> [1] 0 Try : write(b(c)) ? << BUILTIN CALL >> b(c) [1] 0 Succ : write(b(c)) [1] 0 Succ : trace,a(b(c)) yes LOOP = 4 ||?-
このように、述語a/1の定義中の変数Xは、トレース実行時には項b(c)にユニファイされるので、各局面で表示される節やゴールでは項b(c)に置き換えられた形で表示されています。
一方、上の例には含まれませんが、未代入の変数の場合は、元々の変数名の後に「_NN」(アンダスコア+領域の取られたセル番号)が付加された形で表示されます。同じ節中でこの形の表示が同じものは同じ変数を表すと考えてください。他の節に同名の変数があっても、セル番号が違ってきますから、別物であることが容易に分かります。
デバッガーから出力される情報は、Matchとそれ以外の局面で内容が異なります。以下にそれぞれの場合を説明しますが、ここに書かれているのは「標準的なdebug情報」についての内容です。それ以外の場合については、「6-4-3.(2)出力される情報量の選択」を参照してください。
即ち、Try、Succ、Fail、Pop、Retryの場合の出力情報は以下の通りです。
[シリアルナンバー] スタックの深さ 局面名 : ゴール
ここで、シリアルナンバーは「インタプリテーションが始まってからこのゴールを実行するまでの実際のゴールの積層数」です。
この値と次の「先祖系列スタックの深さ」の差が大きいほど、このゴールの手前に非決定性のゴールが多く含まれていることになります。
スタックの深さは「このゴールからトップゴールまでの先祖系列スタックの深さ」を表しています。(6-5-2.コマンド解説Pコマンド参照)
[15] 1 Try:queen1(4,[4,3,2,1],[],Ⅹ,[],[])?
局面名は「Try」です。
シリアルナンバーが「15」で、スタックの深さは「1」です。
「?」を出力してコマンド(「6-5.デバッガのコマンド」参照)を要求しています。
Matchの場合の出力情報は以下の通りです。
Match:頭部がゴールと単一化された節
<例>
Match : put([4,3,2,1],[],L_14) :-
select([4,3,2,1],S_22,D_24),
safe([],S_22,S_22),
put(D_24,[S_22],L_14).
ここではデバッガが情報を出力する局面を変更したり、情報量を変える方法を説明します。
通常はすべての局面で情報が出力されますが、 「Try、 Match、Succ、Fail、Pop、Retry」のうち特定の局面に達したときのみ出力するようにleash1/1」によって設定することもできます。
「leash1(整数)」を実行すると、整数を2進数で表したときのビット0~5 (ビット0が最下位ビット:least significant bit)の6ビットの対応するビットが1である局面についてのみ出力されるようになります。ビットと局面の対応は以下の通りです。
Version 8 からは、r,p,f,s,m,t を含むアトムによる設定を追加しました。
r,p,f,s,m,t が、Retry Pop Fail Succ Match tryに対応。順序は問わず、それ以外の文字は無視します。
?- leash1(rfpt). <= retry,fail,pop,try の意味 2'111001 と同義です。
ビット | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|
局面 | Retry | Pop | Fail | Succ | Match | Try |
例えば、「leash1(4)」を実行すると「Succ」のときにしか出力しません。
また、「leash1(63)」を実行するとすべての局面について出力されます。
【注意】
組込述語については、その種類(「5-10-1.組込述語」参照)にかかわらずヒープ領域に節の実体が無い為、その実行過程は表示されません。この場合「《BUILTIN CALL》」と表示されます。
また、 ヒープ領域に定義された述語(定義述語)であっても、決定性述語(代替節のない述語)の場合は、6つの局面のうち「Retry」と「Pop」の情報は出力されません。
トレースの際に出力する情報量は組込述語 leash2/1 で選択することができます。引数には0~3の整数値を与えます。数値の意味は以下の通りです。何もしなければ、デフォルトは1(標準的な情報出力)に設定されています。「6-4-2.局面ごとの出力情報」で解説した内容がこれに当たります。
0:最小のdebug情報 | |||
Match以外の局面でのゴール表示は「述語名/アリティ」のみ。 Matchの局面での節は何も出力されません。 |
|||
1:標準的なdebug情報 | |||
「6-4-2.局面ごとの出力情報」を参照してください。 | |||
2:最大のdebug情報 | |||
標準的なdebug情報に加え、局面表示の直後に以下のような情報が出力されます。
【記述例】 | ?- listing. | a:- b,c. | a:- c. | c. | ?-leash2(2),trace. yes debug mode on ||?-a. [1] 0 Try : TopGoal :a ? <== トップレベル(コンソール)から入力されたa/0をTryします Match : a/0/1 : <== a/0 の1番目の節にマッチした a :- b, c. [2] 1 Try : a/0/1/1 :b ? <== a/0 の1番目の節の1番目のゴール(b)をTryします [2] 1 Fail : a/0/1/1 :b <== a/0 の1番目の節の1番目のゴール(b)が失敗しました [1] 0 Pop : TopGoal :a <== トップレベル(コンソール)から入力されたa/0をバックトラックします [1] 0 Retry : TopGoal :a ? <== トップレベル(コンソール)から入力されたa/0をRetryします Match : a/0/2 : <== a/0 の2番目の節にマッチした a :- c. << LAST CALL >> [1] 0 Try : a/0/2/1 :c ? <== a/0 の2番目の節の1番目のゴール(c)をTryします Match : c/0/1 <== c/0 の1番目の節にマッチした c. [1] 0 Succ : a/0/2/1 :c <== a/0 の2番目の節の1番目のゴール(c)が成功しました yes |
|||
3:最大に追加されたdebug情報 | |||
最大のdebug情報(2)に加え、通常は出力されない次のトレースが可能になります。 ・freezeされたゴールのトレース ・AZ-Prologコンパイラ(azpc)のオプション /debug でコンパイルされたコード(Cコード、バイトコード)のトレース |
いずれかを選択するには以下のようにします。
<例:最小のdebug情報の場合>
?- leash2(0).
デバッグモードでは、ゴールから述語を呼出した回数をカウントする「ループカウンタ」が動作します。Prologのコマンドレベルから発せられる質問の実行が終了すると、そのカウントの結果が次の様に表示されます。
<例>queen.plが読み込まれているとします。
||?-q(8). : : No.92 . . . Q . . . . . Q . . . . . . . . . . . . Q . . . Q . . . . . . . . . . Q . . . . . . . . . Q . . . . Q . . . Q . . . . . . . Total = 92 yes LOOP = 116718
この機能を使って例えば、「同じ動作をする二つのプログラムのどちらが動作効率がいいか(どちらのプログラムがコールの回数が少なくてすむか)」というようなプログラム効率の検証ができます。
細かい話ですが、もう一つループカウンタを使っていて少し「あれ?」と思われる事があるかも知れません。
それでは、次の質問を実行してみましょう。
||?-true. yes LOOP = 1 ||?-
これは true/0 という節(組込述語)を1回コールしたのですから納得できます。
問題は次の場合です。
||?-true,true. yes LOOP = 3 ||?-
「true/0」を2回コールしたのですから「LOOP=2」となりそうですね。にも関わらず「LOOP=3」と表示されています。
これは、ゴールの区切りの「,」が文法上引数2個のオペレータとして扱われているため、インタプリタはこれを「','(true, true)」と解釈して、先ずこの「 ','/2 」をコール(実行)してからその引数のコール(実行)にかかる為です。
しかし、実際には上で紹介した様な「どちらが動作効率がいいか」というような相対的に比べる使い方をする場合は関係ない事ではあります。
述語コールカウンタを利用すると、プログラムを実行した際にどの述語(組込、ユーザ定義とも)が何回呼ばれているかをチェックすることができます。
<用途>
など
<使い方>
デバッグモードにおいて、次のような形で質問をします。実行するプログラムをゴールで与えます。通常モードではゴールは実行されますが、述語呼び出し回数の集計情報は表示されません。
|| ?- call_count_check(ゴール).
プログラム(ゴール)が終了してトップレベルに戻った時に、使われた各述語がそれぞれ何回呼び出されたかを表示します。ユーザ述語/システム述語/組込述語(これらについては「5-1.述語の種類」を参照)の順に集計・表示されます。
<使用例>
$ prolog_c -c queen.pl AZ-Prolog Version 8.xx (Linux/x64) Copyright (C) SOFNEC CO., LTD. 1987-2015 | ?-debug. yes debug mode on ||?-call_count_check(q(4)). No.1 . Q . . . . . Q Q . . . . . Q . No.2 . . Q . Q . . . . . . Q . Q . . Total = 2 ===CALL COUNT=== exec_status=succ ==== user ==== safe/3 => 52 select/3 => 49 disp2/1 => 20 disp1/2 => 20 put/3 => 17 disp/2 => 10 generate/2 => 5 queen/2 => 1 === system === === builtin === is/2 => 106 ¥== /2 => 64 write/1 => 38 e_register/3 => 4 nl/0 => 2 yes LOOP = 404 || ?-
デバッガがある局面に達してその局面に関する情報をコンソールに出力した後に「?」を表示した場合には、デバッガに対してコマンドを入力することができます。(逆に、このような場合にしかコマンドを入力することはできません。)
どの局面でコマンド入力を受け付けるかは、設定によって変更することができます。まず、「6-5-1.コマンド入力局面の選択」でこの設定方法を説明し、次に「6-5-2.コマンド解説」で、使用できるデバッガのコマンドについて解説していきます。
デフォルトでは、「Try」「Retry」の2つの局面にインタプリタが達したときのみコマンド入力を受け付けるようになっています。
どの局面に達したときにトレース情報を表示するかを「leash1/1」によって変えられるのと同じように(前項参照)、どの局面のときコマンド入力を受け付けるかも「leash/1」によって変更する事が出来ます。
局面とビットとの対応は、前項の「leash1/1」と同じです。
Version8からは、r,p,f,s,m,t を含むアトムによる設定を追加しました。
r,p,f,s,m,t が Retry Pop Fail Succ Match tryに対応。順序は問わず、それ以外の文字は無視します。
?- leash(rfpt). <= retry,fail,pop,try の意味 2'111001 と同義です。
ビット | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|
局面 | Retry | Pop | Fail | Succ | Match | Try |
また、やはり対応するビットが「1」である局面のときのみコマンド入力を受け付けます。
デバッガのコマンドには以下のようなものがあります。
(インタプリタがどの局面にさしかかっているかによって使えるコマンドも変わってきます。すべての局面では使えないコマンドについては、コマンド解説の最初に「使用可能局面:」でこれを示します。)
creep |
インタプリタが次の局面に達し次第、その局面に関する情報をコンソールに出力します。連続してこのコマンドを使うと、インタプリタの動作に関するすべての情報がコンソールに出力されます。 | ||||
---|---|---|---|---|---|
;(セミコロン) retry |
【使用可能局面】「Match」「Succ」 「Match」において使われた場合には、単一化可能な頭部を持つ別の節を探しにいきます。 「Succ」使われた場合には、表示されたゴールの別の可能性を試します。 どちらの場合にも、その直後の実行に失敗してバックトラックした場合とまったく同じように動きます。そのような状態を強制的に作りだしているわけです。 |
||||
A abort |
アボー卜します。 プログラムの実行を強制的に打ち切り、インタプリタのトップレベルに戻ります。 ただし、「errorset/2」または「consult/1」「reconsult/1」の中でこのコマンドが使われた場合には、そこでトラップされます。 「abort/0」を実行した場合とまったく同じです。 |
||||
B break |
プログラムの実行を中断し、新しいブレークレベルに入ります。 Aコマンドとは異なり、「unbreak/0」によって実行を再開することができます。デバッグの途中で、「leash/1」「leash1/1」などを使ってデバッガの出力情報の量をコントロールしたり、プログラムをエディットしたりする時に使います。 「break/0」を実行した時とまったく同じように動作します。 |
||||
D dump stack |
スタックの最底部から最上部までのゴールを全て木構造で表示します。 ”[シリアルナンバー] スタックの深さ: ゴール”の形式で表示され、”>*”が現在のスタック位置を表します。 [シリアルナンバー] はトレース時の「インタプリテーションが始まってからの実際のゴールの積層数」と同じです。 ”スタックの深さ:”は逆順ですが、Pコマンドと同じ現在ゴールの直系列で「現在ゴールからトップゴールまでの先祖系列の深さ」です。 ”スタックの深さ:”がついていない行はオルタナティブの残っている兄弟ゴールおよびその子孫にあたります。 [シリアルナンバー] はバックトラックパス(番号の小さい方向に向かって順番にバックトラックする)でもあります。 6-5-3. でこのコマンドを使ったスタックの状態を調べる例を挙げています。 <例> [14] 1 Try : e_register(0,N1_10,N1_10+1) ? d <== Dコマンド [1] 0: q(4) [2] ┣ queen(4,[2,4,1,3]) [3] ┃ ┗ put([4,3,2,1],[],[2,4,1,3]) [4] ┃ ┣ select([4,3,2,1],3,[4,2,1]) [5] ┃ ┃ ┗ select([3,2,1],3,[2,1]) [6] ┃ ┗ put([4,2,1],[3],[2,4,1,3]) [7] ┃ ┣ select([4,2,1],1,[4,2]) [8] ┃ ┃ ┗ select([2,1],1,[2]) [9] ┃ ┃ ┗ select([1],1,[]) [10] ┃ ┗ put([4,2],[1,3],[2,4,1,3]) [11] ┃ ┣ select([4,2],4,[2]) [12] ┃ ┗ put([2],[4,1,3],[2,4,1,3]) [13] ┃ ┗ select([2],2,[]) [14]>* 1: ┗ e_register(0,N1_10,N1_10+1) Try [14] 1 Try : e_register(0,N1_10,N1_10+1) ? |
||||
F fail |
【使用可能局面】「Try」「Match」「Retry」 「Try」「Retry」において使われた場合には、表示されたゴールと単一化可能な頭部を持つ節の有無に関わらず直ちに失敗させます。 「Match」の場合には、表示された節も含めて、ゴールと単一化可能な頭部を持つ節は1つもなかったかのように動作します。 |
||||
H help |
それぞれの局面で使用可能なコマンドの表をコンソールに出力します。 | ||||
I inspect |
該当ゴールを含む節の定義に変数があり、その変数が束縛されている、または制約されているときその情報を表示します。
なお、定義側変数が虚変数の場合、虚変数の出現順にシーケンシャル番号を付与し区別しています。 <例> |?-listing. turukame(Turu,Kame,Foot,Head) :- Turu in 0..Head, Kame in 0..Head, Foot#=Turu*2+Kame*4, Head#=Turu+Kame. yes |?-trace. yes ||?-turukame(X,Y,14,5). [1] 0 Try : turukame(X_7,Y_9,14,5) ? Match : turukame(X_7,Y_9,14,5) :- X_7 in 0..5, Y_9 in 0..5, 14#=X_7*2+Y_9*4, 5#=X_7+Y_9. [2] 1 Try : X_7 in 0..5 ? i <== Iコマンド Foot = 14 <== 定義節の変数:Foot は 14に束縛されています Head = 5 <== 定義節の変数:Head は 5に束縛されています [2] 1 Try : X_7 in 0..5 ? << BUILTIN CALL >> [2] 1 Succ : _30 in 0..5 [2] 1 Try : Y_9 in 0..5 ? i <== Iコマンド Turu = _30 { [0..5]::true } <== 定義節の変数:Turu は 0..5 に制約されています Foot = 14 Head = 5 [2] 1 Try : Y_9 in 0..5 ? << BUILTIN CALL >> [2] 1 Succ : _46 in 0..5 [2] 1 Try : 14#=_30*2+_46*4 ? i <== Iコマンド Turu = _30 { [0..5]::true } Kame = _46 { [0..5]::true } Foot = 14 Head = 5 [2] 1 Try : 14#=_30*2+_46*4 ? << BUILTIN CALL >> [2] 1 Succ : 14#=_30*2+_46*4 [2] 1 Try : 5#=_30+_46 ? i <== Iコマンド Turu = _30 { [1,3,5]::$clp_area_propagation(7,[1,2],[_30,_46]),! } <== 制約が狭まりました Kame = _46 { [1..3]::$clp_area_propagation(7,[1,2],[_30,_46]),! } <== 制約が狭まりました Foot = 14 Head = 5 [2] 1 Try : 5#=_30+_46 ? <<BUILTIN CALL >> [2] 1 Succ : 5#=3+2 <== この制約で値が求まりました << LAST CALL >> [1] 0 Succ : turukame(3,2,14,5) X = 3, Y = 2 yes |
||||
J jump |
【使用可能局面】「Try」「Match」 「Try」のとき、この述語を実行せずに決定性成功をします。 「Match」のとき、この節のボディーゴールを実行せずに成功します。 |
||||
L leap |
スパイまたはマーク(Sコマンド参照)がかかっているゴールに関する処理をインタプリタが行うまでデバッガの表示を打ち切ります。スパイまたはマークをかけた述語に関する情報のみを表示させたいときには連続してこのコマンドを使います。 | ||||
N notrace |
トレースまたはスパイポイントを解除します。ただしデバッグモードは解除されません。 | ||||
O step return |
親スタックにマークを付け「Lコマンド」の処理を実行します。トレース中にステップを追わなくてもよい節に入り込んでしまったときに使います。 | ||||
P parent |
先祖である節をすべて表示します。 (節1の本体にあるゴールと、単一化可能な頭部を持つ節2をインタプリタが見つけ、その実行に取り掛かったとします。そのようなとき、節1は節2の親であると呼びます。親および親の親、そのまた親の親・・・・を先祖と呼びます。) <例> | ?-[-'queen.pl']. yes | ?-spy select. yes debug mode on ||?-q(4). [4] 3 Try : select([4,3,2,1],S_70,D_72) ? p <== Pコマンドで先祖である節をすべて表示します 3: select([4,3,2,1],S_70,D_72) 2: put([4,3,2,1],[],L_8) :- <== select/3の親はput/3です *> select([4,3,2,1],S_70,D_72), <== ここをTry中 safe([],S_70,S_70), put(D_72,[S_70],L_8). 1: queen(4,L_8) :- <== put/3の親はqueen/2です generate(4,[4,3,2,1]), *> put([4,3,2,1],[],L_8). <== ここをTry中 0: q(4) :- <== queen/2の親はq/1です e_register(0,0,0), *> queen(4,L_8), <== ここをTry中 e_register(0,N1_10,N1_10+1), M_12 is N1_10+1, write('No.'), write(M_12), nl, disp(L_8,4), fail. Goal: ?- q(4). <== q/1を呼び出したトップレベルのゴールです [4] 3 Try : select([4,3,2,1],S_70,D_72) ? |
||||
Q mark&leap |
【使用可能局面】「Try」「Retry」 スタックにマークを付け「Lコマンド」の処理を実行します。 |
||||
R redo |
【使用可能局面】「Fail」 Failした該当ゴールを再Tryします。Failした原因を再検証したり、Breakしてプログラムを追加してから再実行させるなどの用途があります。 |
||||
S mark/unmark |
【使用可能局面】「Try」「Retry」 スタックにマークを付けます。 既にマークがついているスタックではマークを削除します。 |
||||
W where |
「Match」の場合には、Matchした述語定 義の位置(述語名/アリティ/マッチした節の順位)が表示されます。 Match以外の局面(Try,Retry,Succ,Fail,Pop)では、ゴールの位置(親述語名/親述語 アリティ/親述語の節順位/節中のゴール順位)が表示されます。 <例> | a:- b. % a/0/1 述語名=a,アリティ=0 ,第1節 | a:- c. % a/0/2 述語名=a,アリティ=0 ,第2節 ||?-a. [1] 0 Try :a ? Match : a :- b. ? w <== Wコマンド a/0/1 <== a/0 の第1節にマッチしました [2] 1 Try :b ? w <== Wコマンド a/0/1/1 <== a/0 の第1節の1番目のゴール(b)をTryしています |
Prologの理解を深めるため、前節で説明したDコマンドを使って、スタックの状態を追ってみます。
<例1>次のようなシンプルなプログラムで説明します。
%%%%%%%%%%%%%%% %% tree.pl %% a:-b,c. % b かつ c が真ならば、a は真である。 b:-d,e. % d かつ e が真ならば、b は真である。 e:-f. % f が真ならば、e は真である。 d. % d は真である。 d. % d は真である。(オルタナティブ。下図では d'で表現) f. % f は真である。 f. % f は真である。(オルタナティブ。下図では f'で表現) c. % c は真である。
このプログラムで a が真か調べる過程は次のように木構造の成長で表現できます。
?-a. =STEP1================ 0: [1]a Try =STEP2================ 0: [1]a / 1: [2]b Try =STEP3================ 0: [1]a / 1: [2]b / 2: [3]d(d') Try = Succ =STEP4================ 0: [1]a / 1: [2]b ∧ 2: [3]d(d') [4]e Try =STEP5================ 0: [1]a / 1: [2]b ∧ 2: [3]d(d') [4]e / 3: [5]f(f') Try = Succ =STEP6================ 0: [1]a / 1: [2]b ∧ 2: [3]d(d') [4]e Succ / 3: [5]f(f') =STEP7================ 0: [1]a ∧ 1: [2]b [6]c Try <== (Fコマンドで強制的にFailさせてみます) ∧ ┃ 2: [3]d(d') [4]e ┃ / ┃バックトラック 3: [5]f(f')┣━┛ =STEP8================ 0: [1]a / 1: [2]b ∧ 2: [3]d(d') [4]e / 3: [5]f' ReTry = Succ (オルタナティブがないのでスタックからPOPします) =STEP9================ 0: [1]a / 1: [2]b ∧ 2: [3]d(d') [4]e Succ (オルタナティブがないのでスタックからPOPします) =STEP10================ 0: [1]a ∧ 1: [2]b [4]c Try = Succ (C/Rで次に進ませます) / 2: [3]d(d') ================= yes
ここで、
[数値]は「シリアルナンバー」(インタプリテーションが始まってからの実際のゴールの起動順番)、縦座標の”数値:”はその行のゴールの「先祖系列の深さ」です。
プログラムは[1]->[2]->[3]->[4]->[5]->[6]の順に働き、[6]c Tryで強制的にFailすると辿った道筋の逆順、[6]->[5]->[4]->[3]->[2]->[1]の順にバックトラックします。
これをトレース過程にDコマンドを織り込みながらスタック状態を調べてみましょう。
Dコマンドの出力は木構造をCUIで表現するために上記のGUI木構造を左右反転し「シリアルナンバー」順に並べ、親子関係を罫線文字で接続しています。
| ?-[-'tree.pl']. yes | ?-leash2(2),trace. yes debug mode on ||?-a. [1] 0 Try : TopGoal :a ? <== 入力ゴール a をTryします Match : a/0/1 : <== a はbとcが真であれば真です a :- b, c. [2] 1 Try : a/0/1/1 :b ? <== a/0アリティ/第1節の第1ゴール、b が真か調べます Match : b/0/1 : <== b はdとeが真であれば真です b :- d, e. [3] 2 Try : b/0/1/1 :d ? d <== b/0アリティ/第1節の第1ゴール、d が真か調べます。#ここでDコマンド [1] 0: a <== 現在ゴールの祖父です [2] 1: ┗ b <== 現在ゴールの親です [3]>* 2: ┗ d Try <== 現在ゴールはここです [3] 2 Try : b/0/1/1 :d ? Match : d/0/1 : <== d は真です d. [3] 2 Succ : b/0/1/1 :d <== d が真であるのが証明されました [4] 2 Try : b/0/1/2 :e ? d <== b/0アリティ/第1節の第2ゴール、e が真か調べます。#ここでDコマンド [1] 0: a <== 現在ゴールの祖父です [2] 1: ┗ b <== 現在ゴールの親です [3] ┣ d <== 現在ゴールの兄です。オルタナティブがあるので、スタックに残っています [4]>* 2: ┗ e Try <== 現在ゴールはここです [4] 2 Try : b/0/1/2 :e ? Match : e/0/1 : <== e は f が真であれば真です e :- f. [5] 3 Try : e/0/1/1 :f ? d <== e/0アリティ/第1節の第1ゴール、fが真か調べます。#ここでDコマンド [1] 0: a <== 現在ゴールの曽祖父です [2] 1: ┗ b <== 現在ゴールの祖父です [3] ┣ d <== 現在ゴールの叔父(父の兄)です。オルタナティブがあるので、スタックに残っています [4] 2: ┗ e <== 現在ゴールの親です [5]>* 3: ┗ f Try <== 現在ゴールはここです [5] 3 Try : e/0/1/1 :f ? Match : f/0/1 : <== f は真です f. [5] 3 Succ : e/0/1/1 :f <== f が真であるのが証明されました [4] 2 Succ : b/0/1/2 :e <== e が真であるのが証明されました [2] 1 Succ : a/0/1/1 :b <== b が真であるのが証明されました [6] 1 Try : a/0/1/2 :c ? d <== a/0アリティ/第1節の第2ゴール、c が真か調べます。#ここでDコマンド [1] 0: a <== 現在ゴールの親です [2] ┣ b <== 現在ゴールの兄です。子にオルタナティブがあるので、スタックに残っています [3] ┃ ┣ d <== 現在ゴールの甥(兄の第1子)です。オルタナティブがあるので、スタックに残っています [4] ┃ ┗ e <== 現在ゴールの甥(兄の第2子)です。子にオルタナティブがあるので、スタックに残っています [5] ┃ ┗ f <== 現在ゴールの大甥(兄の第2子の子)です。オルタナティブがあるので、スタックに残っています [6]>* 1: ┗ c Try <== 現在ゴールはここです [6] 1 Try : a/0/1/2 :c ? f <== #強制的にFailさせてみます [6] 1 Fail : a/0/1/2 :c <== c は偽となります [5] 3 Pop : e/0/1/1 :f <== バックトラックし、先ほどのf の真を取り消します [5] 3 Retry : e/0/1/1 :f ? d <== f の再充足(オルタナティブ)を調べます。#ここでDコマンド [1] 0: a [2] 1: ┗ b [3] ┣ d [4] 2: ┗ e [5]>* 3: ┗ f Retry <== 現在ゴールはスタックをPOPした位置です [5] 3 Retry : e/0/1/1 :f ? Match : f/0/2 : f. [5] 3 Succ : e/0/1/1 :f << LAST CALL >> <== fが再充足しました [4] 2 Succ : b/0/1/2 :e <== fにはオルタナティブがないので、スタックをPOPします [2] 1 Succ : a/0/1/1 :b <== fの親 eにはオルタナティブがないので、スタックをPOPします [4] 1 Try : a/0/1/2 :c ? d <== bがSuccしました [1] 0: a <== 再び c のTryで#Dコマンド、スタックを調べます [2] ┣ b [3] ┃ ┗ d [4]>* 1: ┗ c Try [4] 1 Try : a/0/1/2 :c ? <== c のTry中です、fおよびeのスタックはオルタナティブがないのでスタックから取り除かれています Match : c/0/1 : <== こんどはC/Rコマンドで成功させます c. [4] 1 Succ : a/0/1/2 :c [1] 0 Succ : TopGoal :a yes
<例2>
次にもう少し具体的な例を見てみましょう。
1)バックトラックは「シリアルナンバー」を逆に辿ること、すなわち辿ってきた道筋を戻ることであるのを確認してください。
2)もともとオルタナティブがない組込述語(is/2など)ユーザー定義述語、再試行後にオルタナティブがなくなり決定性終了をしたユーザ定義述語はスタックに積まれない(バックトラックの対象から除かれる)ことを確認してください。
%%%%%%%%%%%%% %% cost.pl %% 購入総額(Yen,[S|Summary]) :- % 購入総額は商品代金+送料である 商品代金(Goods,Summary), 送料(Send,S), Yen is Goods+Send. 商品代金(Price+Cost,[Package,Grade]) :- % 商品代金は商品価格+梱包費である 商品価格(Price,Grade), 梱包費(Cost,Package). 梱包費(Cost,Package) :- % 梱包費は梱包材費用のことである 包装材費用(Cost,Package). 商品価格(1000,傷あり特価品). % 商品は2種類ある 商品価格(4000,特選最上級品). 包装材費用(0, 新聞紙袋簡易包装). % 包装材は2種類ある 包装材費用(100, ダンボール箱梱包). 送料(120,普通便). % 送料は2種類ある 送料(340,速達便).
yes | ?-[-'cost.pl']. yes | ?-leash(trs),trace. yes debug mode on ||?-購入総額(Yen,S). [1] 0 Try : 購入総額(Yen_5,S_7) ? Match : 購入総額(Yen_5,[S_11|Summary_13]) :- 商品代金(Goods_15,Summary_13), 送料(Send_17,S_11), Yen_5 is Goods_15+Send_17. [2] 1 Try : 商品代金(Goods_15,Summary_13) ? d [1] 0: 購入総額(Yen_5,[S_11|Summary_13]) [2]>* 1: ┗ 商品代金(Goods_15,Summary_13) Try [2] 1 Try : 商品代金(Goods_15,Summary_13) ? Match : 商品代金(Price_21+Cost_23,[Package_25,Grade_27]) :- 商品価格(Price_21,Grade_27), 梱包費(Cost_23,Package_25). [3] 2 Try : 商品価格(Price_21,Grade_27) ? d [1] 0: 購入総額(Yen_5,[S_11,Package_25,Grade_27]) [2] 1: ┗ 商品代金(Price_21+Cost_23,[Package_25,Grade_27]) [3]>* 2: ┗ 商品価格(Price_21,Grade_27) Try [3] 2 Try : 商品価格(Price_21,Grade_27) ? Match : 商品価格(1000,傷あり特価品). [3] 2 Succ : 商品価格(1000,傷あり特価品) ? [4] 2 Try : 梱包費(Cost_23,Package_25) ? d [1] 0: 購入総額(Yen_5,[S_11,Package_25,傷あり特価品]) [2] 1: ┗ 商品代金(1000+Cost_23,[Package_25,傷あり特価品]) [3] ┣ 商品価格(1000,傷あり特価品) [4]>* 2: ┗ 梱包費(Cost_23,Package_25) Try [4] 2 Try : 梱包費(Cost_23,Package_25) ? Match : 梱包費(Cost_23,Package_25) :- 包装材費用(Cost_23,Package_25). [5] 3 Try : 包装材費用(Cost_23,Package_25) ? d [1] 0: 購入総額(Yen_5,[S_11,Package_25,傷あり特価品]) [2] 1: ┗ 商品代金(1000+Cost_23,[Package_25,傷あり特価品]) [3] ┣ 商品価格(1000,傷あり特価品) [4] 2: ┗ 梱包費(Cost_23,Package_25) [5]>* 3: ┗ 包装材費用(Cost_23,Package_25) Try [5] 3 Try : 包装材費用(Cost_23,Package_25) ? Match : 包装材費用(0,新聞紙袋簡易包装). [5] 3 Succ : 包装材費用(0,新聞紙袋簡易包装) ? [4] 2 Succ : 梱包費(0,新聞紙袋簡易包装) ? [2] 1 Succ : 商品代金(1000+0,[新聞紙袋簡易包装,傷あり特価品]) ? [6] 1 Try : 送料(Send_17,S_11) ? d [1] 0: 購入総額(Yen_5,[S_11,新聞紙袋簡易包装,傷あり特価品]) [2] ┣ 商品代金(1000+0,[新聞紙袋簡易包装,傷あり特価品]) [3] ┃ ┣ 商品価格(1000,傷あり特価品) [4] ┃ ┗ 梱包費(0,新聞紙袋簡易包装) [5] ┃ ┗ 包装材費用(0,新聞紙袋簡易包装) [6]>* 1: ┗ 送料(Send_17,S_11) Try [6] 1 Try : 送料(Send_17,S_11) ? Match : 送料(120,普通便). [6] 1 Succ : 送料(120,普通便) ? d [1] 0: 購入総額(Yen_5,[普通便,新聞紙袋簡易包装,傷あり特価品]) [2] ┣ 商品代金(1000+0,[新聞紙袋簡易包装,傷あり特価品]) [3] ┃ ┣ 商品価格(1000,傷あり特価品) [4] ┃ ┗ 梱包費(0,新聞紙袋簡易包装) [5] ┃ ┗ 包装材費用(0,新聞紙袋簡易包装) [6]>* 1: ┗ 送料(120,普通便) Succ [6] 1 Succ : 送料(120,普通便) ? [7] 1 Try : Yen_5 is 1000+0+120 ? << BUILTIN CALL >> [7] 1 Succ : 1120 is 1000+0+120 ? d [1] 0: 購入総額(1120,[普通便,新聞紙袋簡易包装,傷あり特価品]) [2] ┣ 商品代金(1000+0,[新聞紙袋簡易包装,傷あり特価品]) [3] ┃ ┣ 商品価格(1000,傷あり特価品) [4] ┃ ┗ 梱包費(0,新聞紙袋簡易包装) [5] ┃ ┗ 包装材費用(0,新聞紙袋簡易包装) [6] ┣ 送料(120,普通便) <== 送料スタックありに注意 [7]>* 1: ┗ 1120 is 1000+0+120 Succ [7] 1 Succ : 1120 is 1000+0+120 ? [1] 0 Succ : 購入総額(1120,[普通便,新聞紙袋簡易包装,傷あり特価品]) ? Yen = 1120, <== 第1解 S = [普通便,新聞紙袋簡易包装,傷あり特価品]; <== 別解要求の入力 [6] 1 Pop : 送料(120,普通便) <== 送料の選択解除 [6] 1 Retry : 送料(Send_17,S_11) ? <== 送料の別可能性試行 Match : 送料(340,速達便). <== 再充足しました [6] 1 Succ : 送料(340,速達便) ? [6] 1 Try : Yen_5 is 1000+0+340 ? << BUILTIN CALL >> [6] 1 Succ : 1340 is 1000+0+340 ? d [1] 0: 購入総額(Yen_5,[速達便,新聞紙袋簡易包装,傷あり特価品]) [2] ┣ 商品代金(1000+0,[新聞紙袋簡易包装,傷あり特価品]) [3] ┃ ┣ 商品価格(1000,傷あり特価品) [4] ┃ ┗ 梱包費(0,新聞紙袋簡易包装) [5] ┃ ┗ 包装材費用(0,新聞紙袋簡易包装) [6]>* 1: ┗ Yen_5 is 1000+0+340 Try <== 送料スタックなしに注意 [6] 1 Try : Yen_5 is 1000+0+340 ? [1] 0 Succ : 購入総額(1340,[速達便,新聞紙袋簡易包装,傷あり特価品]) ? Yen = 1340, <== 第2解 S = [速達便,新聞紙袋簡易包装,傷あり特価品] yes %% 第2解が求まった後、再度別解要求をすると包装材費用の別可能性を試行します %% 次のような問い合わせをするとどのようなTraceになるか試し、なぜそうなるのか考えてください ||?-購入総額(Yen,[速達便,X,特選最上級品]).