1

Following is my knowledge base:

parent(claudia, taiane).
parent(wl, cris).
parent(cris, jim).
parent(cris, pedro).

man(jim).
man(pedro).
man(w).
man(wl).

woman(taiane).
woman(claudia).
woman(cris).

When my main has just one print:

main:-

  man(M),
  print(M), nl, 
  fail.
main:- halt.

when I execute it with swipl -q -s exercise-family-tree.pl -g main I get nonduplicated results (all good). On the other hand, when I query more in my main and have print two times with two different variables as its arguments in my main:

main:-
  %which women have both a father and a son in the database?
  woman(X), parent(X, S), parent(F, X), man(F), man(S), print(X), nl, 
  man(M),
  print(M), nl, 
  fail.
main:- halt.

than the results get duplicated. Why does my code duplicate all the answers in the second case?

The issue is different from the one I raised earlier. Please let any interactive solutions that involve REPL be outside of the scope of this question.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
John Smith
  • 835
  • 1
  • 7
  • 19
  • 1
    Do not use print like this, it isn't meant for that. Maybe `format` instead. Your overall approach (with the query then the fail and the halt) is going to cause you much trouble. I know this isn't what you want to hear but this is why this is a comment and not an answer. – TA_intern Jul 24 '22 at 10:27

3 Answers3

3

than the results get duplicated. Why does my code duplicate all the answers in the second case?

I don't think your problem is anything to do with the top level or way you're running it from the command line, it's that you don't really understand Prolog search, choice points and backtracking and have written a nested loop which prints the same results twice.

The first code acts like (pseudocode):

for M in [jim, pedro, w, wl]:
    print(M)

The second code acts like a nested loop:

for _Child in [jim, pedro]:
    print(chris)          % NB: same 'chris' 
                          %   for both choices of _Child
    for M in [jim, pedro, w, wl]:
        print(M)

In more detail, the first code with man(M), print(M), nl, fail runs like this:

man(jim),   print(jim),   fail,  % backtrack to man(M)
man(pedro), print(pedro), fail,  % backtrack to man(M)
man(w),     print(w),     fail,  % backtrack to man(M)
man(wl),    print(wl),    fail.  % backtrack to man(M)
% no more man(M) choices

The second case, this code:

woman(X), parent(X, S), parent(F, X), man(F), man(S), print(X), nl, 
man(M),
print(M), nl, 
fail.

runs like this:

woman(taiane),  parent(taiane,  ???)  % implicit fail
woman(claudia), parent(claudia, ???)  % implicit fail
woman(chris),parent(chris, jim),parent(wl, chris),man(wl),man(jim),print(chris)
%                          ~~~
% found a solution, now go forward through the man(M) code:

    man(jim),   print(jim),   fail,  % backtrack to man(M)
    man(pedro), print(pedro), fail,  % backtrack to man(M)
    man(w),     print(w),     fail,  % backtrack to man(M)
    man(wl),    print(wl),    fail,  % backtrack to man(M)

    % no more man(M) choices, now the last choicepoint was 
    % at parent(chris, S) which had choice S=jim or S=pedro.
    % Redo that:

woman(chris),parent(chris, pedro),parent(wl, chris),man(wl),man(jim),print(chris)
%                          ~~~~~
% found a solution, now go forward through the man(M) code:

    man(jim),   print(jim),   fail,  % backtrack to man(M)
    man(pedro), print(pedro), fail,  % backtrack to man(M)
    man(w),     print(w),     fail,  % backtrack to man(M)
    man(wl),    print(wl),    fail.  % backtrack to man(M)

    % no more man(M) choices, now parent(chris, S) has
    % no more choices, so end.

So you make two different choices for S from [jim,pedro] by X=chris, parent(X, S) but do not report them, and only report the other choices, for X and M, which are the same in both cases, so it looks like duplication.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
TessellatingHeckler
  • 27,511
  • 4
  • 48
  • 87
1

Your problem is not where you think it is. First, let me show you how to run a program from the command line. This one is a "hello world":

Contents of hello.pl:

main :-
    format("hello~n").

The way to run it from the command line without entering the top level:

$ swipl -g main -t halt hello.pl
hello

Now here is a program that on the interactive top level would backtrack and have multiple solutions. Just to demonstrate, here is the program, in a file print.pl:

main :-
    between(1, 3, X),
    format("~w~n", [X]).

and if you do it from the top level, you get:

?- [print].
true.

?- main.
1
true ;
2
true ;
3
true.

Fine. If we do it as the "hello world" above however:

$ swipl -g main -t halt print.pl
1

Now, I will add a "fail" like you did. Here is my new program in a file printfail.pl:

main :-
    between(1, 3, X),
    format("~w~n", [X]),
    fail.

When I run this from the command line:

swipl -g main -t halt printfail.pl
1
2
3
ERROR: -g main: false

Bottom line: throwing away solutions by running the program non-interactively is throwing you off.


And a footnote: The fail causes your program to backtrack even when a solution was found. The backtracking undoes the bindings but cannot undo the side effect.

TA_intern
  • 2,222
  • 4
  • 12
  • Is `swipl -g main -t halt print.pl` equivalent to running `swipl -g main -t print.pl` with `main:- halt.` appended as the last line after `fail.`? – John Smith Jul 24 '22 at 11:23
  • @JohnSmith I now realize that it all started with [this terrible suggestion](https://stackoverflow.com/a/54245495/14411997). The answer warns you it is "hackish" but didn't provide anything better. Stackoverflow is such a wasteland of misguided questions and terrible answers.... :-( – TA_intern Jul 24 '22 at 13:44
  • @JohnSmith to your question, you should try it out and see. – TA_intern Jul 24 '22 at 13:45
0

Try it with main as:

main:-
  %which women have both a father and a son in the database?
  %trace,
  woman(X), parent(X, S), parent(F, X), man(F), man(S), print(X), nl,
  %man(M),
  %print(M), nl,
  fail.
main:- halt.

This shows the duplicate:

$ swipl -q -s parent3.pl -g main
cris
cris

Can use https://www.swi-prolog.org/pldoc/doc_for?object=distinct/2 to remove the duplicate, i.e.:

distinct(X, (woman(X), parent(X, S), parent(F, X), man(F), man(S))), print(X), nl,
brebs
  • 3,462
  • 2
  • 3
  • 12