Multiple instances of Prolog clauses with identical ground head. Any uses besides for this besides (dubious) control of computation?

那年仲夏 提交于 2020-02-25 02:10:11

问题


In SWI Prolog:

Exact clause duplicates are allowed:

a(1).
a(1).
?- a(X).
X = 1 ;
X = 1.

or even:

c :- format("Hello from first c!").
c :- format("Hello from second c!").
Hello from first c!
true ;
Hello from second c!
true.

More generally, so are clauses with identical fully ground heads but differing bodies:

b(1) :- format("Hello from first b!").
b(1) :- format("Hello from second b!").
?- b(1).
Hello from first b!
true ;
Hello from second b!
true.

Clauses with identical non-ground head feel somewhat more reasonable:

p(X) :- format("Yup, this is p(~w)",X).
p(X) :- format("Yup, this is also p(~w)",X).
p(X) :- format("You think you can get rid of good old p(~w) just like that?",X).
?- p('homer simpson').
Yup, this is p(homer simpson)
true ;
Yup, this is also p(homer simpson)
true ;
You think you can get rid of good old p(homer simpson) just like that?
true.
?- p(X).
Yup, this is p(_4782)
true ;
Yup, this is also p(_4782)
true ;
You think you can get rid of good old p(_4782) just like that?
true.

This covers the reasonable case of clauses with a guarded body:

p(X) :- X < 0,   format("It's less than 0: ~w", X).
p(X) :- X =:= 0, format("It's exactly 0: ~w", X).
p(X) :- X > 0,   format("It's larger than 0: ~w", X).

On second thoughts ... we already encounter the ultimate case in the built-in repeat:

?- repeat.
true ;
true ;
true ;
true ; 
… 

Or can construct an intermediate case easily:

h :- member(_,[1,2,3]).
?- h.
true ;
true ;
true.

Somehow textbooks gloss over the fact that predicates have additional semantics: they can not only be false or true for any given ground arguments, but they can actually be true(n) - "true n times, n ≥ 0" .

From a theory standpoint, this is dubious, at least for vanilla classical logic.

On the other hand, it is useful from a computational standpoint for:

  • Side-effects (rather for output than for input, and rather marginally).
  • Control of the computation (i.e. repeat).

Are there any other uses?

I really feel the compiler should flag variable-less cases like a/1, c/0, b/1 above as errors (easy to detect), and even repeat/0 should probably have a value: repeat(Count). Anything which redoes successfully should NOT redo successfully in the same context on exactly the same ground arguments. It has the same squishy feeling of irrelevancy as a(X) :- b(Y). b(Z). Brrr!.


回答1:


Yes, for diagnostic purposes in pure programs. By duplicating a clause you can answer the question how often the clause contributes to solutions.

That is, you count the answers/solutions you get from a query and compare these to the same program plus the duplicated clause. If the number of (redundant) solutions now increases you know that this clause contributes. The ld factor tells you how often.

Note that a tracer cannot tell you this as easily.




回答2:


Some observations in the particular case of duplicated clauses.

If the repeated clauses for predicate a/1 are found in a Logtalk object or in a Prolog module that can be compiled as an object, the Logtalk linter can warn you of duplicated clauses. For example, given the following module:

:- module(duplicates, []).

a(1).
a(1).

We get:

?- set_logtalk_flag(duplicated_clauses, warning).
true.

?- {duplicates}.
*     Duplicated clause: a(1)
*       first found at or above line 3
*       while compiling object duplicates
*       in file /Users/pmoura/duplicates.lgt at or above line 4
*     
% [ /Users/pmoura/duplicates.lgt loaded ]
% 1 compilation warning
true.

This particular lint warning is usually turned off by default due to its computational cost. If you load the tutor tool prior to the compilation of the module, you will get instead:

?- {tutor(loader)}.
...
% (0 warnings)
true.

?- set_logtalk_flag(duplicated_clauses, warning).
true.

?- {duplicates}.
*     Duplicated clause: a(1)
*       first found at or above line 3
*       while compiling object duplicates
*       in file /Users/pmoura/Desktop/duplicates.lgt at or above line 4
*     Duplicated clauses are usually a source code editing error and can
*     result in spurious choice-points, degrading performance. Delete or
*     correct the duplicated clause to fix this warning.
*     
% [ /Users/pmoura/Desktop/duplicates.lgt loaded ]
% 1 compilation warning
true.

The warning is useful and proved itself worth in non-trivial codebases.




回答3:


@false mentions in his answer using duplicated clauses to "answer the question how often the clause contributes to solutions." He also mentions "that a tracer cannot tell you this as easily".

A better solution to this use case is to use a ports profiler tool as found in e.g. ECLiPSe and Logtalk. No need to duplicate clauses to gather that information.

As an example, consider the following pure program, saved in a pure.pl file:

a :- b, c.

b.
b :- d.

c.

d.

Let's include this code in an object. We can define it in a source file:

:- object(pure).

    :- set_logtalk_flag(debug, on).

    :- public(a/0).
    :- include('pure.pl').

:- end_object.

Alternatively, we can create pure as a dynamic object:

?- create_object(pure, [],  [set_logtalk_flag(debug,on), public(a/0), include('pure.pl')], []).
true.

Either way, after loading or creating the pure object and loading the ports_profiler tool, we can e.g. query all solutions for the a/0 predicate and then print the profiling data:

?- {ports_profiler(loader)}.
...
% (0 warnings)
true.

?- pure::a.
true ;
true.

?- ports_profiler::data.
-------------------------------------------------------------------
Entity  Predicate    Fact  Rule  Call  Exit *Exit  Fail  Redo Error
-------------------------------------------------------------------
pure    a/0             0     1     1     1     1     0     1     0
pure    b/0             1     1     1     1     1     0     1     0
pure    c/0             2     0     2     2     0     0     0     0
pure    d/0             1     0     1     1     0     0     0     0
-------------------------------------------------------------------
true.

The table displays port crossing information for all predicates used in the query to the a/0 predicate. For example, that the predicate c/0 as called twice (for the two solutions to a/0) and exited deterministically. On the other hand, the predicate b/0 was called once by succeeded twice (on of them non-deterministically) due to backtracking.

For details on the portable port_profiler tool and a discussion on the insights that it can provide, see:

https://logtalk.org/manuals/devtools/ports_profiler.html



来源:https://stackoverflow.com/questions/60282952/multiple-instances-of-prolog-clauses-with-identical-ground-head-any-uses-beside

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!