14

プログラミングクラスの宿題を終えました。リストを反転する Prolog プログラムを作成することになっていました。ただし、正確に機能する理由を理解するのに苦労しています。

%1. reverse a list
%[a,b,c]->[c,b,a]

%reverse(list, rev_List).
reverse([],[]).  %reverse of empty is empty - base case
reverse([H|T], RevList):-
    reverse(T, RevT), conc(RevT, [H], RevList).  %concatenation

この場合、RevT とは正確には何ですか? T の逆または指定されたリストの残りを表すことになっていることはわかっていますが、何にも割り当てていないため、どのように値を持つことができるかわかりません。RevListと同じ目的を果たしますが、再帰呼び出しごとに機能しますか?

また、conc() 関数呼び出しで H だけでなく [H] を使用する必要があるのはなぜですか? H はリストの先頭 (例: [H]) を参照していませんか? それとも、リストの先頭にある項目 (単に H) を参照しているだけですか?

これを解決するのを手伝ってください。このタイプのプログラミングの背後にあるロジックを理解するのに苦労しています。

4

7 に答える 7

20

あなたの解決策は説明しました:空のリストを逆にすると、空のリストが得られます。リスト [H|T] を逆にすると、 T を逆にして [H] を連結したリストになります。再帰節が正しいことを確認するには、リスト [a,b,c,d] を検討してください。このリストの末尾を逆にすると、 [d,c,b] が得られます。これを [a] と連結すると [d,c,b,a] が得られます。これは [a,b,c,d] の逆です。

別の逆の解決策:

 reverse([],Z,Z).

 reverse([H|T],Z,Acc) :- reverse(T,Z,[H|Acc]).

電話:

?- reverse([a,b,c],X,[]).

詳細については、http: //www.learnprolognow.org/lpnpage.php?pagetype=html&pageid=lpn-htmlse25を参照してください。

于 2013-10-19T22:58:48.370 に答える
16

プロローグ リストは単純なデータ構造です。./2

  • 空のリストはアトム[]です。
  • 1 つの要素のリスト は[a]、実際には次の構造です.(a,[])
  • 2 つの要素のリストは、[a,b]実際には次の構造です。.(a,.(b,[]))
  • 3 つの要素のリストは、[a,b,c]実際には次の構造です。.(a,.(b,.(c,[])))
  • 等々。

角括弧表記は、丸括弧の入力に時間を費やさないようにするための構文糖衣です。目に優しいことは言うまでもありません。

これから、リストの先頭(最も外側の./2構造のデータ) とリストの末尾(その最も外側の./2データ構造に含まれるサブリスト) の概念を取得します。

これは基本的に、C の古典的な片方向リストで見られるのと同じデータ構造です。

struct list_node
{
  char payload ;
  struct list_node *next ;
}

ここで、nextポインターは NULL または別のリスト構造です。

したがって、そこから、reverse/2 の単純な [単純な] 実装を取得します。

reverse( [] , []    ) .  % the empty list is already reversed.
reverse[ [X] , [X]  ) .  % a list of 1 item is already reversed. This special case is, strictly speaking, optional, as it will be handled by the general case.
reverse( [X|Xs] , R ) :- % The general case, a list of length >= 1 , is reversed by
  reverse(Xs,T) ,        % - reversing its tail, and
  append( T , [X] , R )  % - appending its head to the now-reversed tail
  .                      %

この同じアルゴリズムは、従来のプログラミング言語で片方向リストを逆にする場合にも機能します。

ただし、このアルゴリズムはあまり効率的ではありません。まず、O(n 2 ) の動作を示します。また、末尾再帰ではありません。つまり、十分な長さのリストはスタック オーバーフローを引き起こします。

プロローグ リストにアイテムを追加するには、リスト全体をトラバースする必要があることに注意してください。プロローグ リストの構造により、先頭に追加するのは簡単な操作です。次のように、既存のリストにアイテムを簡単に追加できます。

prepend( X , Xs , [X|Xs] ) .

プロローグの一般的なイディオムは、アキュムレータでワーカー述語を使用することです。これにより、実装がはるかに効率的になり、(おそらく) 理解しやすくなります。ここでは、アキュムレータを空のリストとしてシードすることにより、リストを逆にします。ソースリストを反復処理します。ソース リストでアイテムに遭遇すると、それを反転リストの先頭に追加し、その結果、反転リストを生成します。reverse/2

reverse(Xs,Ys) :-            % to reverse a list of any length, simply invoke the
  reverse_worker(Xs,[],Ys) . % worker predicate with the accumulator seeded as the empty list

reverse_worker( []     , R , R     ).    % if the list is empty, the accumulator contains the reversed list
reverse_worker( [X|Xs] , T , R     ) :-  % if the list is non-empty, we reverse the list
  reverse_worker( Xs , [X|T] , R )       % by recursing down with the head of the list prepended to the accumulator
  .

これreverse/2で、O(n) 時間で実行される実装ができました。また、末尾再帰です。つまり、スタックを使い果たすことなく、任意の長さのリストを処理できます。

于 2013-10-21T19:26:03.757 に答える
3

代わりに、はるかに理解しやすい DCG の使用を検討してください。

reverse([])     --> [].
reverse([L|Ls]) --> reverse(Ls), [L].

例:

?- phrase(reverse([a,b,c]), Ls).
Ls = [c, b, a].
于 2013-10-20T00:49:03.957 に答える
1

この場合、RevT とは正確には何ですか? T の逆または指定されたリストの残りを表すことになっていることはわかっていますが、何にも割り当てていないため、どのように値を持つことができるかわかりません。RevListと同じ目的を果たしますが、再帰呼び出しごとに機能しますか?

Prolog の変数は、関係の引数の「プレースホルダー」です。私たちが知っていることは、呼び出しが成功した後、指定された引数がその関係に対して保持されているということです。

呼び出しが成功すると、RevT値が返されます。具体的には、リストが空でない場合conc(RevT, [H], RevList)、 callの最後の引数になります。それ以外の場合は、空のリストになります。

また、conc() 関数呼び出しで H だけでなく [H] を使用する必要があるのはなぜですか? H はリストの先頭 (例: [H]) を参照していませんか? それとも、リストの先頭にある項目 (単に H) を参照しているだけですか?

はい、H はリストの最初の項目(通常はelementと呼ばれます) を参照します。次に、リスト間の別の関係である conc/3 で要求されるように、(1 つの要素のみの) リストになるようにそれを「再形成」する必要があります

于 2013-10-20T06:17:50.477 に答える
0

以下は reverse/2 の典型的な実装です。ただし、「非終了」とマークされている問題があります。

?- ['/dev/tty'] .

reverse(_source_,_target_) :-
reverse(_source_,_target_,[])  .

reverse([],_target_,_target_)  .

reverse([_car_|_cdr_],_target_,_collect_) :-
reverse(_cdr_,_target_,[_car_|_collect_])  .

end_of_file.

.

?- reverse([],Q) .
Q = []

?- reverse([a],Q) .
Q = [a]

?- reverse([a,b],Q) .
Q = [b,a]

?- reverse([a,b,c],Q) .
Q = [c,b,a]

?- reverse(P,[]) .
P = [] ? ;
%% non-termination ! %%
^CAction (h for help): a

?- reverse(P,[a]) .
P = [a] ? ;
%% non-termination ! %%
^CAction (h for help): a

?- reverse(P,[a,b]) .
P = [b,a] ? ;
%% non-termination ! %%
^CAction (h for help): a

?- reverse(P,[a,b,c]) .
P = [c,b,a] ? ;
%% non-termination ! %%
^CAction (h for help): a
于 2019-03-24T07:05:05.570 に答える