Limit Printing of the prolog facts to THREE times? - prolog

Say I have 10 facts path(X) in the database.
How do I limit it to printing the first 3 from the database?
I have tried using recursive technique but it seems that it will only unify with the first one.
All helps appreciated.

There are two possibilities. Both need to utilize extra-logical properties of Prolog.
(1) You collect all facts in a list and then print the first N items of this list using recursion. This approach is clear, but it could be inefficient when there are many facts (e.g. thousands) and only few of them will be written (e.g. 3).
print1(N) :-
findall(path(X), path(X), List),
print1(List, N).
print1([], N) :- !.
print1([H|T], N) :-
writeln(H),
N1 is N - 1, N1 > 0,
print1(T, N1).
?- print1(3).
(2) You can use a retractable counter and a failure driven loop. This approach is less elegant than the first one but will be more efficient in the case there are many facts and only few of them are written.
:- dynamic count/1.
print2(N) :-
assert(count(N)), !,
path(X), writeln(path(X)),
retract(count(K)),
K1 is K - 1,
(K1 > 0 -> assert(count(K1)); true, !),
fail.
?- print2(3).
Addition: Printing first N "smallest" facts in ascending order:
print_sorted(N) :-
findall(path(X), path(X), List),
msort(List, SortedList),
print1(SortedList, N).

If you are sure, that there are at least 3 facts and you don't mind if all Solutions are created:
three_results(A,B,C) :- findall(X,path(X),[A,B,C|_]).
You can also use something like:
n_results(0,A,A) :- !.
n_results(N,A,R) :- path(X), (member(X,A) -> fail; true), N1 is N - 1, n_results(N1,[X|A],R).
three_results(X) :- n_results(3,[],X).

Related

Correct way to terminate common list length len/2 relation

The following is a common definition for len/2 which relates a list to its length, found in many introductory guides and textbooks.
% length of a list
len([], 0).
len([_H|T], L) :- len(T, M), L is M+1.
This works well for queries where the first parameter is provided as a sufficiently instantiated list, and the second parameter is an unbound variable. For example, the following asks "how long is the list [a,b,c]?"
?- len([a,b,c], L).
L=3
This query terminates, as expected.
However, in the opposite direction, we can ask "which list has length 3?". This is done by providing the first parameter as an unbound variable, and the second parameter with a value 3.
?- len(X, 3).
X = [_1688, _1694, _1700]
.. infinite loop ..
Here prolog provides the expected answer, a list containing 3 items. The 3 items are unbound variables, which is logically correct because they can take any value and the len/2 relation will still be true.
However - prolog backtracks and tries to find further solutions. This leads it to try ever longer lists, a logically infinite loop which will eventually crash a finite computer.
Question: What is the correct way to adjust the prolog program defining len/2 to avoid this non-termination?
I would like answers that explain why tactical procedural approaches, such as using cuts, is not as good as a program that applies a further logical constraint, perhaps suggesting the current program is logically correct but not complete.
UPDATE:
The following use of a cut seems to be a simple, but procedural, way to achieve the desired bi-directionality with termination.
% length of a list
len([], 0).
len([_H|T], L) :- len(T, M), L is M+1.
len2(X,Y) :- len(X,Y), !.
Test 1:
?- len2([a,b,c],L).
L =3
(terminates)
Test 2:
?- len2(X,3).
X = [_1598, _1604, _1610]
(terminates)
I'm still learning prolog so I'd appreciate, as per the original question, if and how this is logically impure. The above tests suggests it is "pure" in the sense it terminates and works bidirectionally.
I think this is a reasonable solution, to satisfy the usual requirements of:
Not spiralling off into infinity unexpectedly
Reasonable performance, elegance, determinism, readability and code-reusability
list_length(Lst, Len) :-
( nonvar(Len)
-> integer(Len),
Len #>= 0
; true
),
once_only_if(
(is_list(Lst) ; nonvar(Len)),
list_length_(Lst, 0, Len)
).
list_length_([], Len, Len).
list_length_([_|T], Upto, Len) :-
Upto1 is Upto + 1,
list_length_(T, Upto1, Len).
% Generic and reusable predicates are below
once_only_if(OnceIf, Goal) :-
call_t(OnceIf, Bool),
t_once(Bool, Goal).
call_t(Goal, Bool) :-
% Don't use *-> because Goal might contain ";" alternatives
( call(Goal)
-> Bool = true
; Bool = false
).
t_once(true, Goal) :-
call(Goal),
% Don't backtrack into Goal
!.
t_once(false, Goal) :-
call(Goal).
Results in swi-prolog:
?- list_length(L, -1).
false.
?- list_length(L, 1.5).
false.
?- list_length([a,b,c], N).
N = 3.
?- list_length(L, 3).
L = [_, _, _].
Recursion (unless TRO, tail-recursive optimization, i.e. a loop) is to be avoided unless the programmer really couldn't be bothered, because recursion is slower than non-recursion in any language (a very rare exception is permute).
Since length/2 is used so commonly in Prolog, it is performance-optimized to the extreme - swi-prolog implements it natively in C, and here's Scryer.
A better example for avoiding non-termination whilst also avoiding cuts is https://stackoverflow.com/a/74130437/
Performance comparison with:
len([], 0).
len([_H|T], Len) :-
len(T, Len0),
Len is Len0 + 1.
len2(Lst, Len) :-
len(Lst, Len), !.
?- garbage_collect, length(Lst, 5_000_000), time(list_length(Lst, Len)).
% 5,000,006 inferences, 0.096 CPU in 0.096 seconds (100% CPU, 52325193 Lips)
?- garbage_collect, length(Lst, 5_000_000), time(len2(Lst, Len)).
% 10,000,002 inferences, 2.495 CPU in 2.500 seconds (100% CPU, 4008165 Lips)

How to assert new data dynamically in Prolog

I have previously define a few facts dynamically as below.
% declare dynamic facts
:- dynamic title/2.
:- dynamic author/2.
:- dynamic publisher/2.
:- dynamic price/2.
:- dynamic call_number/2.
:- dynamic edition/2.
:- dynamic data_disk/2.
and assert these facts every time the program runs
:- assert(title(book1, 'Elementary Statistics')).
:- assert(title(book2, 'Statistics for Engineers')).
:- assert(title(book3, 'Statistics for Engineers and Scientists')).
:- assert(title(book4, 'IT in Language Learning')).
:- assert(author(book1, 'Patricia Wilson')).
:- assert(author(book2, 'James Mori')).
:- assert(author(book3, 'James Mori')).
:- assert(author(book4, 'O Ivan')).
:- assert(publisher(book1, 'Addison Wesley')).
:- assert(publisher(book2, 'World Scientific')).
:- assert(publisher(book3, 'World Scientific')).
:- assert(publisher(book4, 'Universal Press')).
:- assert(price(book1, 75)).
:- assert(price(book2, 125)).
:- assert(price(book3, 125)).
:- assert(price(book4, 5)).
:- assert(call_number(book1, 'QA373')).
:- assert(call_number(book2, 'QA673')).
:- assert(call_number(book3, 'QA674')).
:- assert(call_number(book4, 'QA007')).
:- assert(edition(book1, 1)).
:- assert(edition(book2, 3)).
:- assert(edition(book3, 2)).
:- assert(edition(book4, 1)).
:- assert(data_disk(book1, 'No')).
:- assert(data_disk(book2, 'Yes')).
:- assert(data_disk(book3, 'Yes')).
:- assert(data_disk(book4, 'No')).
As you can see the facts are in a certain order
book1
book2
book3
book4
How can I get the last X, where X is bookX, and increment by 1 so that the new book to be inserted will always be (X+1)?
You found one solution (i.e., count the existing facts, and add 1), which works but has one major drawback: It makes the run time of adding a single new fact proportional to the number of already asserted facts. This means that asserting a series of N facts takes time proportional to N2.
Ideally, we would like to have a situation where asserting a single fact is in 𝒪(1), and asserting N facts is thus in 𝒪(N).
One way to achieve this is to reconsider your initial representation of books.
For example, suppose that you present your books like this (some data omitted for brevity):
book([title('Elementary Statistics'),
author('Patricia Wilson'),
price(75)]).
book([title('Statistics for Engineers'),
author('James Mori'),
publisher('World Scientific')]).
Note that this representation allows us to omit fields that are only present in some of the books. Other representations would also make sense.
We can easily fetch all these facts with findall/3:
?- findall(Book, book(Book), Books).
That's linear in the number of such facts.
Further, let us define assert_book_/3 as follows:
assert_book_(Book, N0, N) :-
memberchk(title(Title), Book),
memberchk(author(Author), Book),
assertz(title(N0,Title)),
assertz(author(N0,Author)),
N #= N0 + 1.
For the sake of example, I am focusing on the title and author. I leave extending this as an exercise.
The arguments of this predicate are:
the book to be asserted, represented as a list of attributes
the current index N0
the next index N1, which is simply one greater than N0.
Now the main point: These arguments are in a suitable order to fold the predicate over a list of books, using the meta-predicate foldl/4:
?- findall(Book, book(Book), Books),
foldl(assert_book_, Books, 1, _).
After running this query, we have:
?- title(N, T).
N = 1,
T = 'Elementary Statistics' ;
N = 2,
T = 'Statistics for Engineers'.
And similar facts for author/2 in the database:
?- author(N, T).
N = 1,
T = 'Patricia Wilson' ;
N = 2,
T = 'James Mori'.
Thus, we have used foldl/4 to implicitly keep track of the running index we need, and achieved a solution that has the desired running time.
Note that there is also a wise-cracking solution for your task:
assert_title(Book, Title) :-
atom_concat(book, N0, Book),
atom_number(N0, N),
assertz(title(N, Title)).
This is obviously not what you looking for, but would work for the example you show, if you use for example:
:- assert_title(book1, 'Elementary Statistics').
:- assert_title(book2, 'Statistics for Engineers').
Now we have again:
?- title(N, Title).
N = 1,
Title = 'Elementary Statistics' ;
N = 2,
Title = 'Statistics for Engineers'.
The joke here is that you have actually entered the running index already, and we can use atom_concat/3 to obtain it:
?- atom_concat(book, N0, book1),
atom_number(N0, N).
N0 = '1',
N = 1.
;-)
I cleared my mind at the nearest Starbucks and came up with the simplest answer.
add_book :-
aggregate_all(count, title(_,_), Count),
NewCount is Count + 1,
atom_concat('book', NewCount, NewBook).
The aggregate_all function will count number of title predicates that's available in my knowledge base and some calculation will be performed.
I am open to better suggestion though, do reply if you have a better approach.

CLP in Prolog involving consecutive sums in a list

Example of my CLP problem (this is a small part of a larger problem which uses the clpfd library):
For a list of length 5, a fact el_sum(Pos,N,Sum) specifies that the N consecutive elements starting from position Pos (index from 1) have sum equal to Sum. So if we have
el_sum(1,3,4).
el_sum(2,2,3).
el_sum(4,2,5).
Then [1,2,1,4,1] would work for this example since 1+2+1=4, 2+1=3, 4+1=5.
I'm struggling with how to even start using the el_sum's to find solutions with an input list [X1,X2,X3,X4,X5]. I'm thinking I should use findall but I'm not really getting anywhere.
(My actual problem is much bigger than this so I'm looking for a solution that doesn't just work for three facts and a small list).
Thanks!
You are mixing here the monotonic world of constraints with some non-monotonic quantification. Don't try to mix them too closely. Instead, first transform those facts into, say, a list of terms.
el_sums(Gs) :-
G = el_sum(_,_,_),
findall(G, G, Gs).
And then, only then, start with the constraint part that will now remain monotonic. So:
?- el_sums(Gs), length(L5,5), maplist(l5_(L5), Gs).
l5_(L5, el_sum(P, N, S)) :-
length([_|Pre], P),
length(Cs, N),
phrase((seq(Pre),seq(Cs),seq(_)), L5),
list_sum(Cs,S).
seq([]) --> [].
seq([E|Es]) --> [E], seq(Es).
Not sure this will help, I don't understand your workflow... from where the list do come ? Anyway
:- [library(clpfd)].
el_sum(Pos,N,Sum) :-
length(L, 5),
L ins 0..100,
el_sum(Pos,N,Sum,L),
label(L), writeln(L).
el_sum(P,N,Sum,L) :-
N #> 0,
M #= N-1,
Q #= P+1,
el_sum(Q,M,Sum1,L),
element(N,L,T),
Sum #= Sum1 + T.
el_sum(_P,0,0,_L).
yields
?- el_sum(1,2,3).
[0,3,0,0,0]
true ;
[0,3,0,0,1]
true ;
...

How do I skip an element that already have been printing prolog

Im trying to do an exercise in Prolog like this: i introduce this list [10,20, 10, 20, 30], and the program shows :
10 - 2 time; 20 - 2 times; 30 - 1 times.
Here is my code:
conta(_,[], 0).
conta(X, [X|T], N) :- conta(X,T, N2), N is N2 + 1 .
conta(X, [Y|T], N) :- X \= Y, conta(X,T,N).
aux([],[]).
aux([X|L],L1):-conta(X,L1,C),write(X),write(C), write('vezes'),aux(L,L1).
But the result is this:
10 - 2times 20 -2time 10-2times 20-2times 30-1 time
false.
He shows the element the number of time that the element is in the list.
Any help, Please!!
In your problem statement you are interspersing the pure relation with side-effects. While you might solve the problem in this manner you will see only very few of Prolog's interesting properties. Instead, try to formulate your problem as a pure relation. So imagine, you have it already implemented, and formulate some queries for it:
?- list_vezes([10,20, 10, 20, 30], [10-2,20-2,30-1]).
true.
The following solution counts and removes the corresponding elements. It has some n2 runtime.
list_vezes([], []).
list_vezes([E|Es1], [E-N|Vezes]) :-
n_occ(E, Es1,Es2, 1,N),
list_vezes(Es2, Vezes).
n_occ(_, [],[], N,N).
n_occ(E, [E|Es0],Es, N0,N) :-
N1 is N0+1,
n_occ(E, Es0,Es, N1,N).
n_occ(E, [F|Es0],[F|Es], N0,N) :-
dif(F,E),
n_occ(E, Es0,Es, N0,N).
In many Prolog systems prolog-dif is a built-in. See the link what to do if you do not have it.
Now, if you still want to print out the text you said, you might do this with this new list:
printitem(E-N) :-
writeq(E-N), write(' times\n').
compter_et_imprimer(L) :-
list_vezes(L, Vezes),
maplist(printitem, Vezes).

Depth limited search in prolog (vanilla meta-interpreter)

I need to modify the vanilla meta-interpreter in order to make a search with limited depth. I'm using the following code for testing my sollution:
value(wire1,1).
connected(wire2, wire1).
connected(wire3, wire2).
connected(wire4, wire3).
connected(wire5, wire4).
connected(wire6, wire5).
connected(wire7, wire6).
connected(wire8, wire7).
connected(wire9, wire8).
value(W,X):-connected(W,V), value(V,X).
And the target is that something like:
solve(value(w9,X), 3). /*depth =3, it should return false*/
solve(value(w9,X), 20). /*depth=20 is enought for returning X=1*/
By the way my code is
solve(true,_):-!.
solve((A,B),D) :-!, solve(A,D), solve(B,D).
solve(A,D) :- clause(A, B),solve(B,D2),D=D2+1,D>0).
But it don't work property. Can you help me? Thanks a lot in advance
An interesting page on metaprogramming came from a good developer: Markus Triska.
Here (A Couple of Meta-interpreters in Prolog) you find both theory and practice. For instance:
... Another group of extensions aims to improve the incomplete default computation strategy. We start from an MI that limits the depth of the search tree:
mi_limit(Goal, Max) :-
mi_limit(Goal, Max, _).
mi_limit(true, N, N).
mi_limit((A,B), N0, N) :-
mi_limit(A, N0, N1),
mi_limit(B, N1, N).
mi_limit(g(G), N0, N) :-
N0 > 0,
mi_clause(G, Body),
N1 is N0 - 1,
mi_limit(Body, N1, N).
You were almost there. Only the last clause needs a slight rearranging:
solve(A, D) :- clause(A, B), D1 is D - 1, D1 > 0, solve(B, D1).
?- solve(value(wire9, X), 9). ===> false.
?- solve(value(wire9, X), 10). ===> X = 1.
dls(X,X,[X],L):-
L >0 goal(X).
dls(X,Y,[A|p],L):-
L > 0 ,goal(Y) ,
move(X,Y),
L1 is L - 1 ,
dls(Z,Y ,P,L1).

Resources