Prolog not showing full list of answer - prolog

When I run query human(Who). on the below .pl file
human(ann).
human(george).
human(mike).
I only get back Who = ann .
Instead of
Who = ann ;
Who = george ;
Who = mike.
Am using prolog 6.6.6. How do I get it to show the full list?

The answer you got was the following. Do you note the space before the dot?
Who = ann .
^ SPACE!!!
This space means: The query was aborted. Maybe you typed return. Or maybe you have a somewhat illconfigured terminal.
To better check this, try:
?- X = 1 ; X = 2 ; X = 3.
There you should get all three answers, too. If not, it is definitely your terminal

What you are seeing is the default behaviour of prolog.
The query
?- findall(Object,Goal,List).
Should work for you.
Eg.
findall(X, human(X), L).
It will populate the list with all possible answers.

Related

How do I convert a string into ascii in amzi! prolog?

so I tried converting strings into ascii with the name/2 and char_codes/2 and so on, but I keep getting things like [0w0061,0w0062,0w0063]
for example.
?- name(abc,A).
A = [0w0061,0w0062,0w0063]
I want the returned list to be [97,98,99].
any help?
ok so I got it.
if I tried for example
?- name(abc,A).
and it returned this:
A = [0w0061,0w0062,0w0063]
I took a returned member and subtracted 0 from it. It looks like this.
for example:
?- name(a, A).
A = [0w0061] ,
no
?- A is 0w0061 - 0.
A = 97

PROLOG result vs true

I have the next code in a file called "testing.pl":
fact(1).
fact(2).
fact(3).
funcA(X) :- funcB(X).
funcB(X) :- fact(X).
testing :- funcA(_X).
Then, in SWI-Prolog interpreter I query funcA(X). and the output is:
X = 1 ;
X = 2 ;
X = 3.
But, whenever I query testing. the output is:
true ;
true ;
true.
So, my questions is:
How can I use the conclusion of a rule as a premise of another rule (funcA(X) at the right of testing), but having the same effect as if I query that premise (funcA(X))?
In the example above, I would like to write testing. at some point of my "testing.pl" file and to get the funcA(X) to do the same like when I query with the interpreter, so funcB(X) will check for all values that X can take from fact(N) and return it.
My desire result would be to write testing. and to get on screen:
X = 1 ;
X = 2 ;
X = 3.
Thanks.
You can manually print anything on the terminal, using for example predicates like portray_clause/1, format/2 etc.
The only additional piece you need is a way to force backtracking over all answers. One way to do it is using false/0.
So, in your case, you can write for example:
testing :-
funcA(X),
format("X = ~q ;~n", [X]),
false.
And now invoking testing/0 yields, without any further interaction:
?- testing.
X = 1 ;
X = 2 ;
X = 3 ;
In addition, the predicates now fails, so you also get false/0 if you use it interactively (from the toplevel), but not if you invoke testing from the shell via swipl -g testing ....
Also take a look at the important variable_names/1 option that is available to use the intended variable names.
I lave tailoring this output to your exact use case as an exercise. Ideally, the output should be a proper Prolog term, so that you can actually test it easily by reading it with read/1 and comparing it to a reference result.

Subtracting variables from a Prolog knowledge base [duplicate]

Trying to create a predicate (timePeriod/2) that calculates the time period between two dates for a specific fact. I've managed to do this by myself, but face issues when 'other answers' exist in the same list (i.e. easier to explain with examples).
I have the following knowledge-base facts;
popStar('Jackson',1987,1991).
popStar('Jackson',1992,1996).
popStar('Michaels',1996,2000).
popStar('Newcastle',2000,2007).
popStar('Bowie',2008,2010).
And the following function, calculates the time between dates for a specific fact (as per below).
Predicate (timePeriod/2) -
timePeriod(PS,X) :-
bagof((Name,Start,End),popStar(Name,Start,End),PSs),X is End-Start+1)
Using Bowie as an example; it returns X=3 (which is correct).
However, when there is repetition in the list, with more than one answer available, the predicate just states 'false'. Using the facts 'Jackson' as an example, I want to be able to calculate both of the time periods for both facts; at the same time.
So, if the predicate would work for both of the Jackson facts, the predicate timePeriod would state X=10.
Would really appreciate if anyone could suggest what to change in order for this to work correctly.
Thanks.
You probably don't quite understand what foreach/3 does. I don't think I fully understand foreach/3 either. I know for sure that it is not the same as say:
for each x in xs:
do foo(x)
Another thing: "tuples" in Prolog are not what you might expect, coming from a language like Python or Haskell. This: (a,b,c) is actually this: ','(a,','(b,c)). Much better is to use a flat term, the generic form would be triple(a,b,c). For a pair, the idiom is First-Second.
So, you can simplify your call to bagof/3 to this:
?- bagof(From-To, pop_star(Name, Start, End), Ts).
Name = 'Bowie',
Ts = [2008-2010] ;
Name = 'Jackson',
Ts = [1987-1991, 1992-1996] ;
Name = 'Michaels',
Ts = [1996-2000] ;
Name = 'Newcastle',
Ts = [2000-2007].
Once you have a list as above, you need to sum the differences, which would be maybe something like:
periods_total(Ps, T) :-
maplist(period_length, Ps, Ls),
sum_list(Ls, T).
period_length(From-To, Length) :-
Length is To - From + 1.
And then you can query like this:
?- bagof(From-To, pop_star('Jackson', From, To), Ps), periods_total(Ps, T).
Ps = [1987-1991, 1992-1996],
T = 10.
?- bagof(From-To, pop_star(Name, From, To), Ps), periods_total(Ps, T).
Name = 'Bowie',
Ps = [2008-2010],
T = 3 ;
Name = 'Jackson',
Ps = [1987-1991, 1992-1996],
T = 10 ;
Name = 'Michaels',
Ps = [1996-2000],
T = 5 ;
Name = 'Newcastle',
Ps = [2000-2007],
T = 8.
SWI-Prolog has a nice library to handle aggregation: it builds upon standard 'all solutions' predicates like findall/3,setof/3,bagof/3, so you should first grasp the basic of these (as Boris explained in his answer). With the library, a single query solves your problem:
timePeriod(PS,X) :-
aggregate(sum(P), B^E^(popStar(PS,B,E),P is E-B+1), X).

Aggregate solution over multiple facts

Trying to create a predicate (timePeriod/2) that calculates the time period between two dates for a specific fact. I've managed to do this by myself, but face issues when 'other answers' exist in the same list (i.e. easier to explain with examples).
I have the following knowledge-base facts;
popStar('Jackson',1987,1991).
popStar('Jackson',1992,1996).
popStar('Michaels',1996,2000).
popStar('Newcastle',2000,2007).
popStar('Bowie',2008,2010).
And the following function, calculates the time between dates for a specific fact (as per below).
Predicate (timePeriod/2) -
timePeriod(PS,X) :-
bagof((Name,Start,End),popStar(Name,Start,End),PSs),X is End-Start+1)
Using Bowie as an example; it returns X=3 (which is correct).
However, when there is repetition in the list, with more than one answer available, the predicate just states 'false'. Using the facts 'Jackson' as an example, I want to be able to calculate both of the time periods for both facts; at the same time.
So, if the predicate would work for both of the Jackson facts, the predicate timePeriod would state X=10.
Would really appreciate if anyone could suggest what to change in order for this to work correctly.
Thanks.
You probably don't quite understand what foreach/3 does. I don't think I fully understand foreach/3 either. I know for sure that it is not the same as say:
for each x in xs:
do foo(x)
Another thing: "tuples" in Prolog are not what you might expect, coming from a language like Python or Haskell. This: (a,b,c) is actually this: ','(a,','(b,c)). Much better is to use a flat term, the generic form would be triple(a,b,c). For a pair, the idiom is First-Second.
So, you can simplify your call to bagof/3 to this:
?- bagof(From-To, pop_star(Name, Start, End), Ts).
Name = 'Bowie',
Ts = [2008-2010] ;
Name = 'Jackson',
Ts = [1987-1991, 1992-1996] ;
Name = 'Michaels',
Ts = [1996-2000] ;
Name = 'Newcastle',
Ts = [2000-2007].
Once you have a list as above, you need to sum the differences, which would be maybe something like:
periods_total(Ps, T) :-
maplist(period_length, Ps, Ls),
sum_list(Ls, T).
period_length(From-To, Length) :-
Length is To - From + 1.
And then you can query like this:
?- bagof(From-To, pop_star('Jackson', From, To), Ps), periods_total(Ps, T).
Ps = [1987-1991, 1992-1996],
T = 10.
?- bagof(From-To, pop_star(Name, From, To), Ps), periods_total(Ps, T).
Name = 'Bowie',
Ps = [2008-2010],
T = 3 ;
Name = 'Jackson',
Ps = [1987-1991, 1992-1996],
T = 10 ;
Name = 'Michaels',
Ps = [1996-2000],
T = 5 ;
Name = 'Newcastle',
Ps = [2000-2007],
T = 8.
SWI-Prolog has a nice library to handle aggregation: it builds upon standard 'all solutions' predicates like findall/3,setof/3,bagof/3, so you should first grasp the basic of these (as Boris explained in his answer). With the library, a single query solves your problem:
timePeriod(PS,X) :-
aggregate(sum(P), B^E^(popStar(PS,B,E),P is E-B+1), X).

convert compound to atom in prolog

I want to use atom_chars/2 on the expression of 3+4, but I get
ERROR: atom_chars/2: Type error: 'atom' expected, found '3+4' (a compound).
I'm thinking that if I can add " " on both sides of the compound, it would work, e.g.
atom_chars("3+4", Result).
but I don't know how I can do that, or is there other approaches to do this?
Please give me some advice.
EDIT: What I mean is that the input has to be 3+4, instead of '3+4', so what I want to do is to write a predicate before the atom_chars/2 to convert 3+4 to '3+4'.
For instance: for compound2atom(X,Y),
-?compound2atom(3+4,Y).
Y='3+4'.
If you are using SWI-Prolog, there is with_output_to/2 or format/3:
?- with_output_to(atom(A), write(3+4)).
A = '3+4'.
?- with_output_to(chars(C), write(3+4)).
C = ['3', +, '4'].
?- format(atom(A), "~w", [3+4]).
A = '3+4'.
?- format(chars(C), "~w", [3+4]).
C = ['3', +, '4'].
But if you look hard enough you should be able to find some predicate that does that, for example term_to_atom/2.
My personal preference leans towards format/3.

Resources