Why do we use '!' in the prologue

This is the code I'm trying to understand.

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

What is the use of '!' and the predicate p (,) in the code above. OR Can someone just add comments at every step of the above code so that I can understand. Thanks.

-2
source share
2 answers

Beginners tend to use !/0 because they are not aware of their negative effects.

This is because most Prolog tutorials that are popular with beginners are pretty bad and often contain incorrect and misleading information about !/0 .

There is a great @false answer when to use !/0 . So: not .

Instead, focus on a declarative description of what is taking place and try to make the description elegant and general using pure and monotonous methods such as constraints, pure presentation, ...

+3
source

There are many questions in your program. Cuts are not even a serious problem. Please bring me a broom .

Interface cleaning

What is the exact interface you use? The goal of co(Xs) now is to create a side effect. Otherwise, it may succeed or fail for a specific list. But no more than that. Nevertheless, this side effect is not needed at all - and for most situations this is not a very useful approach, since such a program is practically inaccessible and does not lend itself to any logical reasoning. You need to leave a hole so that some result disappears from the relationship. Add another argument and delete the write/1 target in co/3 .

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

Now you can test the program only using the top-level shell. You do not need any harnesses or sandboxes to check the "exit". He is, easily in a separate argument.

Clear program structure

Further co/3 . It is best to clarify the intention by separating a little from the problems and making these additional arguments a little more obvious. D means dictionary. Another good name would be KVs list of values ​​(plural s ) of key-value pairs. Pay attention to the numbering of the various states: they begin with D0 , D1 , ... and at the end there is D Thus, if you start writing a rule, you can put D0,D already in your head, not knowing how many states you need in this rule.

 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 = [XV|D1]. nn(K, D0,D) :- D = [K-1|D0]. p(XY,[XY|R],R):- !. p(X,[H|Y], [H|Z]) :- p(X,Y,Z). 

co/3 now more clearly reveals his intent. This somehow associates the list items with some state, which is "updated" for each item. There is a word for this: this is the left fold. And there is even a predicate: foldl/4 . Thus, we could define co/3 as:

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

or better get rid of co/3 in general:

 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). 

Note that so far I have not even touched on your abbreviations, now these are their last moments ...

Removing excess cuts

The abbreviation in p/3 serves no purpose. In any case, after the goal p/3 there is a cut. Then XY not required in p/3 , you can safely replace it with another variable. In short, p/3 now the predicate select/3 from Prolog Prolog.

 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 = [KV|D1]. nn(K, D0,D) :- D = [K-1|D0]. 

This remaining incision cannot be removed so easily: it protects the alternative sentence from use if KV not found in D However, there are even better ways to express this.

Replace cuts with (\+)/1

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

Now every rule claims what it wants for itself. This means that now we are free to reorder these rules. Call it superstition, but I prefer:

 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 = [KV|D1]. 

Clean with dif/2

To turn this into a true attitude, we need to get rid of this denial. Instead of saying that there is no solution, we can instead require that all keys (the key is the first argument in Key-Value ) be different from 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,[KV|D]) :- select(K-V0, D0,D), V is V0+1. 

Using lambdas nokey(K, D) becomes a maplist(K+\(Kx-_)^dif(Kx,K), D)

To summarize, we now have:

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

So what is this relation: The first argument is a list, and the second argument is a Key-Value list with each element and the number of entries in the list.

+3
source

Source: https://habr.com/ru/post/983977/


All Articles