-2

これは私が理解しようとしているコードです。

co(X) :- co(X,[],L).
co([],A,A):- write(A).
co([X|Xs], A, L) :- p(X-Z,A,R), !, Z1 is Z+1, co(Xs, [X-Z1|R], L). 
co([X|Xs], A, L) :- co(Xs, [X-1|A], L). 

p(X-Y,[X-Y|R],R):- !.
p(X,[H|Y], [H|Z]) :- p(X,Y,Z).

「!」の使用は何ですか 上記のコードの述語 p(,,) 。または、上記のコードのすべてのステップにコメントを追加して、理解できるようにすることはできますか。ありがとう。

4

2 に答える 2

4

プログラムには、対処すべきことがたくさんあります。カットは大きな懸念事項ではありません。ほうきを持ってきてください。

インターフェイスをクリーンアップする

あなたが求めている正確なインターフェースは何ですか?現在の目的はco(Xs)、副作用を生み出すことです。それ以外の場合は、特定のリストに対して成功または失敗する可能性があります。しかし、それ以上ではありません。しかし、この副作用はまったく必要ありません。ほとんどの状況では、このようなプログラムは実質的に再利用できず、論理的な推論に反するため、有用なアプローチではありません。リレーションの外に何らかの結果が潜むようにするには、穴を残す必要があります。別の引数を追加し、 のゴールを削除write/1co/3ます。

co(Xs, D) :-
   co(Xs, [], D).

これで、最上位シェルだけでプログラムをテストできます。「出力」を確認するためのハーネスやサンドボックスは必要ありません。それは別の議論で容易にそこにあります。

プログラム構造をクリーンアップする

次はco/3自分です。ここで最善の方法は、関心事を少し分けて意図を明確にし、これらの余分な議論をもう少し意図を明らかにすることです。D辞書の略です。もう 1 つの適切な名前は、キーと値のペアのKVsリスト (複数形) を意味します。さまざまな状態がどのように番号付けされているかに注意してください。それらは, , ... でs始まり、最後に があります。このように、ルールを書き始めると、そのルールに必要な状態の数がわからなくても、頭に入れておくことができます。D0D1DD0,D

co([], D,D).
co([X|Xs], D0,D) :-
   nn(X, D0,D1),
   co(Xs, D1,D).

nn(K, D0,D) :-
   p(K-V0,D0,D1), !,
   V is V0+1,
   D = [X-V|D1].
nn(K, D0,D) :-
   D = [K-1|D0].

p(X-Y,[X-Y|R],R):- !.
p(X,[H|Y], [H|Z]) :- p(X,Y,Z).

co/3その意図をより明確に明らかにするようになりました。リストの要素を、要素ごとに「更新」された状態に何らかの形で関連付けます。これには言葉があります。これは左折です。そして、その述語さえあります: foldl/4. したがって、次のように定義することもできますco/3

co(Xs, D0,D) :-
   foldl(nn, Xs, D0,D).

co/3または完全に取り除く方が良い:

co(Xs, D) :-
   foldl(nn, Xs, [], D).

foldl(_C_3, [], S,S).
foldl(C_3, [X|Xs], S0,S) :-
   call(C_3, X, S0,S1),
   foldl(C_3, Xs, S1,S).

これまでのところ、私はあなたのカットに触れていないことに注意してください。これらは今、最後の瞬間です...

リムーバー余分なカット

カットインは何のp/3役にも立ちません。p/3とにかくゴール直後にカットがあります。次に、X-Yは必要ありませんp/3。別の変数に安全に置き換えることができます。つまり、Prolog プロローグp/3の述語になりました。select/3

select(E, [E|Xs], Xs).
select(E, [X|Xs], [X|Ys]) :-
   select(E, Xs, Ys).

nn(K, D0,D) :-
   select(K-V0, D0,D1), !,
   V is V0+1,
   D = [K-V|D1].
nn(K, D0,D) :-
   D = [K-1|D0].

この 1 つの残りのカットは、そう簡単に削除することはできません。代替節が使用されないように保護しK-VますD。ただし、これを表現するより良い方法はまだあります。

カットを置き換える(\+)/1

nn(K, D0,D) :-
   select(K-V0, D0,D1),
   V is V0+1,
   D = [K-V|D1].
nn(K, D0,D) :-
   \+select(K-_, D0,_),
   D = [K-1|D0].

ここで、各ルールはそれ自体が何を望んでいるかを示します。つまり、これらのルールの順序を自由に変更できるようになりました。迷信と呼んでいますが、私は次のことを好みます。

nn(K, D0,D) :-
   \+select(K-_, D0,_),
   D = [K-1|D0].
nn(K, D0,D) :-
   select(K-V0, D0,D1),
   V is V0+1,
   D = [K-V|D1].

で浄化dif/2

これを真の関係にするためには、この否定を取り除く必要があります。解決策がないと言う代わりに、代わりにすべてのキー (キーは の最初の引数Key-Value) が と異なることを要求できますK

nokey(_K, []).
nokey(K, [Kx-|KVs]) :-
   dif(K, Kx),
   nokey(K, KVs).

nn(K, D,[K-1|D]) :-
   nokey(K, D).
nn(K, D0,[K-V|D]) :-
   select(K-V0, D0,D),
   V is V0+1.

lambdasの助けを借りnokey(K, D)て、maplist(K+\(Kx-_)^dif(Kx,K), D)

要約すると、次のようになりました。

co(Xs, D) :-
   foldl(nn, Xs, [], D).

nn(K, D,[K-1|D]) :-
   maplist(K+\(Kx-_)^dif(Kx,K), D).
nn(K, D0,[K-V|D]) :-
   select(K-V0, D0,D),
   V is V0+1.

では、この関係とは何ですか。最初の引数はリストで、2 番目の引数は Key-Value リストで、各要素とリスト内の出現回数が含まれます。

于 2014-11-14T18:46:19.017 に答える