Related
The following Prolog program defines a predicate deleted/3 for deleting all the occurrences of the item passed in first argument from the list passed in second argument and results in the list passed in third argument:
deleted(_, [], []).
deleted(X, [X|Y], Z) :-
deleted(X, Y, Z).
deleted(U, [V|W], [V|X]) :-
deleted(U, W, X),
U \= V.
It works with queries in this argument mode:
?- deleted(a, [a, b, a], [b]).
true
; false.
It also works with queries in this argument mode:
?- deleted(X, [a, b, a], [b]).
X = a
; false.
It also works with queries in this argument mode:
?- deleted(a, [a, b, a], Z).
Z = [b]
; false.
It also works with queries in this argument mode:
?- deleted(X, [a, b, a], Z).
X = a, Z = [b]
; X = b, Z = [a, a]
; false.
It also works with queries in this argument mode:
?- deleted(a, Y, Z).
Y = Z, Z = []
; Y = [a], Z = []
; Y = [a, a], Z = []
; Y = [a, a, a], Z = []
; Y = [a, a, a, a], Z = []
; …
It also works with queries in this argument mode:
?- deleted(X, Y, Z).
Y = Z, Z = []
; Y = [X], Z = []
; Y = [X, X], Z = []
; Y = [X, X, X], Z = []
; Y = [X, X, X, X], Z = []
; …
But it exhausts resources with queries in this argument mode:
?- deleted(a, Y, [b]).
Stack limit (0.2Gb) exceeded
Stack sizes: local: 0.2Gb, global: 28.1Mb, trail: 9.3Mb
Stack depth: 1,225,203, last-call: 0%, Choice points: 1,225,183
Possible non-terminating recursion:
[1,225,203] deleted(a, _1542, [length:1])
[1,225,202] deleted(a, [length:1|_1584], [length:1])
It also exhausts resources with queries in this argument mode:
?- deleted(X, Y, [b]).
Stack limit (0.2Gb) exceeded
Stack sizes: local: 0.2Gb, global: 28.1Mb, trail: 9.3Mb
Stack depth: 1,225,179, last-call: 0%, Choice points: 1,225,159
Possible non-terminating recursion:
[1,225,179] deleted(_1562, _1564, [length:1])
[1,225,178] deleted(_1596, [length:1|_1606], [length:1])
How to implement list item deletion for all argument modes?
Intro
Pure Prolog conjunctions and disjunctions are, in fact, commutative and associative.
This allows us to ignore clause and goal order, provided that all answer sequences are finite.
When queries have infinite solution sets, Prolog may need to systematically enumerate infinite answer sequences.
The fix
To help Prolog find answers for above “problematic” queries, swap the two recursive rules:
deleted(_,[],[]).
deleted(U,[V|W],[V|X]) :- % this clause was last
dif(U,V),
deleted(U,W,X).
deleted(X,[X|Y],Z) :-
deleted(X,Y,Z).
Demo
Let’s run your queries again with above code changes!
The finite ones work like before1:
?- deleted(a,[a,b,a],[b]). % Q1
true
; false.
?- deleted(X,[a,b,a],[b]). % Q2
X = a
; false.
?- deleted(a,[a,b,a],Z). % Q3
Z = [b]
; false.
?- deleted(X,[a,b,a],Z). % Q4
Z = [a,b,a], dif(X,a), dif(X,b)
; Z = [a, a], X=b
; Z = [ b ], X=a
; false.
Some infinite ones were okay before—they still are:
?- deleted(a,Y,Z). % Q5
Y = Z, Z = []
; Y = Z, Z = [_A], dif(_A,a)
; Y = Z, Z = [_A,_B], dif(_A,a), dif(_B,a)
; Y = Z, Z = [_A,_B,_C], dif(_A,a), dif(_B,a), dif(_C,a)
; …
?- deleted(X,Y,Z). % Q6
Y = Z, Z = []
; Y = Z, Z = [_A], dif(X,_A)
; Y = Z, Z = [_A,_B], dif(X,_A), dif(X,_B)
; Y = Z, Z = [_A,_B,_C], dif(X,_A), dif(X,_B), dif(X,_C)
; …
Some infinite ones used to be “problematic”—not anymore:
?- deleted(a,Y,[b]). % Q7
Y = [b]
; Y = [b,a]
; Y = [b,a,a]
; Y = [b,a,a,a]
; …
?- deleted(X,Y,[b]). % Q8
Y = [b], dif(X,b)
; Y = [b,X], dif(X,b)
; Y = [b,X,X], dif(X,b)
; Y = [b,X,X,X], dif(X,b)
; Y = [b,X,X,X,X], dif(X,b)
; …
Analysis
Now, ?- deleted(X,Y,[b]). makes Prolog give us answers.
But why did we run out-of-memory?
How come it did not work?
To explain this, let’s take a step back: the default / vanilla / out-of-the-box prolog-toplevel of many2 Prolog systems initially runs each query until it discovers either (0) finite failure or (1) the 1st answer3.
Before the fix, we observed neither. Why not?
Why no finite failure?
deleted(a,[a,b,a],[b]) holds true.
Therefore, the more general deleted(X,Y,[b]) must not fail.
Why no (1st) answer?
Prolog proceeds depth-first, top-down, left-to-right.
So when …
?- deleted(X,Y,[b]).
… “meets” …
deleted(X,[X|Y],Z) :-
deleted(X,Y,Z).
… inside the Prolog machine, the following happens:
A choicepoint is created for saving the information—to be used upon backtracking—that another clause could have been selected.
Next, Prolog proceeds with a recursive goal which is just like the original one: we are no closer to an answer, as the 3rd argument—the only instantiated one—stays exactly the same.
Eventually, this loop runs out of memory—which is exactly the behavior that you observed.
If we swap two recursive clauses, the following clause becomes our top-most recursive clause:
deleted(U,[V|W],[V|X]) :-
dif(U,V),
deleted(U,W,X).
Now, something is going on with the 3rd argument: Prolog recursively walks down the single-linked list until [] (or a free logical variable) is reached. Only then can Prolog make use of the fact deleted(_,[],[]). and give us an answer.
Footnotes:
In fact better, as we preserve logical-purity by using dif/2 for expressing syntactic term inequality.
More on dif/2: prolog-dif
All command-line-interface based Prolog systems I have ever used.
Not stopping at the 1st answer is way better for code quality—particularly in regard to universal termination properties.
GUPU, an excellent environment specialized for Prolog and constraint programming courses, does the right thing—by default!
“Answer substitutions are displayed in chunks of five.”
The following prolog logic
memberd(X, [X|_T]).
memberd(X, [Y| T]) :- dif(X,Y), memberd(X, T).
will produce
?- memberd(a, [a, b, a]).
true
?- memberd(X, [a, b, a]).
X = a ;
X = b ;
false.
?- memberd(X, [a, b, a, c, a, d, b]).
X = a ;
X = b ;
X = c ;
X = d ;
false.
is there prolog logic that can be used to produce the same result without using when() or dif() function or anything from a loaded prolog library. Just using pure logic?
To answer your question literally, just use:
?- setof(t, member(X, [a,b,a]), _).
X = a
; X = b.
However, some answers will be suboptimal:
?- setof(t,member(a,[a,X]),_).
true
; X = a. % redundant
... whereas memberd/2 answers in perfection:
?- memberd(a,[a,X]).
true
; false.
In fact, if you use library(reif) with
memberd(E, [X|Xs]) :-
if_(E = X, true, memberd(E, Xs) ).
you get the best answer possible:
?- memberd(a,[a,X]).
true.
I am doing some easy exercises to get a feel for the language.
is_list([]).
is_list([_|_]).
my_flatten([],[]).
my_flatten([X|Xs],RR) :-
my_flatten(Xs,R),
(is_list(X), !, append(X,R,RR); RR = [X | R]).
Here is a version using cut, for a predicate that flattens a list one level.
my_flatten([],[]).
my_flatten([X|Xs],RR) :-
my_flatten(Xs,R),
if_(is_list(X), append(X,R,RR), RR = [X | R]).
Here is how I want to write it, but it does not work. Neither does is_list(X) = true as the if_ condition. How am I intended to use if_ here?
(Sorry, I somewhat skipped this)
Please refer to P07. It clearly states that it flattens out [a, [b, [c, d], e]], but you and #Willem produce:
?- my_flatten([a, [b, [c, d], e]], X).
X = [a,b,[c,d],e]. % not flattened!
And the solution given there succeeds for
?- my_flatten(non_list, X).
X = [non_list]. % unexpected, nothing to flatten
Your definition of is_list/1 succeeds for is_list([a|non_list]). Commonly, we want this to fail.
What you need is a safe predicate to test for lists. So let's concentrate on that first:
What is wrong with is_list/1 and if-then-else? It is as non-monotonic, as many other impure type testing predicates.
?- Xs = [], is_list([a|Xs]).
Xs = [].
?- is_list([a|Xs]). % generalization, Xs = [] removed
false. % ?!? unexpected
While the original query succeeds correctly, a generalization of it unexpectedly fails. In the monotonic part of Prolog, we expect that a generalization will succeed (or loop, produce an error, use up all resources, but never ever fail).
You have now two options to improve upon this highly undesirable situation:
Stay safe with safe inferences, _si!
Just take the definition of list_si/1 in place of is_list/1. In problematic situations, your program will now abort with an instantiation error, meaning "well sorry, I don't know how to answer this query". Be happy for that response! You are saved from being misled by incorrect answers.
In other words: There is nothing wrong with ( If_0 -> Then_0 ; Else_0 ), as long as the If_0 handles the situation of insufficient instantiations correctly (and does not refer to a user defined program since otherwise you will be again in non-monotonic behavior).
Here is such a definition:
my_flatten(Es, Fs) :-
list_si(Es),
phrase(flattenl(Es), Fs).
flattenl([]) --> [].
flattenl([E|Es]) -->
( {list_si(E)} -> flattenl(E) ; [E] ),
flattenl(Es).
?- my_flatten([a, [b, [c, d], e]], X).
X = [a,b,c,d,e].
So ( If_0 -> Then_0 ; Else_0 ) has two weaknesses: The condition If_0 might be sensible to insufficient instantiations, and the Else_0 may be the source of non-monotonicity. But otherwise it works. So why do we want more than that?
In many more general situations this definition will now bark back: "Instantiation error"! While not incorrect, this still can be improved. This exercise is not the ideal example for this, but we will give it a try.
Use a reified condition
In order to use if_/3 you need a reified condition, that is, a definition that carries it's truth value as an explicit extra argument. Let's call it list_t/2.
?- list_t([a,b,c], T).
T = true.
?- list_t([a,b,c|non_list], T).
T = false.
?- list_t(Any, T).
Any = [],
T = true
; T = false,
dif(Any,[]),
when(nonvar(Any),Any\=[_|_])
; Any = [_],
T = true
; Any = [_|_Any1],
T = false,
dif(_Any1,[]),
when(nonvar(_Any1),_Any1\=[_|_])
; ... .
So list_t can also be used to enumerate all true and false situations. Let's go through them:
T = true, Any = [] that's the empty list
T = false, dif(Any, []), Any is not [_|_] note how this inequality uses when/2
T = true, Any = [_] that's all lists with one element
T = true, Any = [_|_Any1] ... meaning: we start with an element, but then no list
list_t(Es, T) :-
if_( Es = []
, T = true
, if_(nocons_t(Es), T = false, ( Es = [_|Fs], list_t(Fs, T) ) )
).
nocons_t(NC, true) :-
when(nonvar(NC), NC \= [_|_]).
nocons_t([_|_], false).
So finally, the reified definition:
:- meta_predicate( if_(1, 2, 2, ?,?) ).
my_flatten(Es, Fs) :-
phrase(flattenl(Es), Fs).
flattenl([]) --> [].
flattenl([E|Es]) -->
if_(list_t(E), flattenl(E), [E] ),
flattenl(Es).
if_(C_1, Then__0, Else__0, Xs0,Xs) :-
if_(C_1, phrase(Then__0, Xs0,Xs), phrase(Else__0, Xs0,Xs) ).
?- my_flatten([a|_], [e|_]).
false.
?- my_flatten([e|_], [e|_]).
true
; true
; true
; ... .
?- my_flatten([a|Xs], [a]).
Xs = []
; Xs = [[]]
; Xs = [[],[]]
; ... .
?- my_flatten([X,a], [a]).
X = []
; X = [[]]
; X = [[[]]]
; X = [[[[]]]]
; ... .
?- my_flatten(Xs, [a]).
loops. % at least it does not fail
In Prolog, the equivalen of an if … then … else … in other languages is:
(condition -> if-true; if-false)
With condition, if-true and if-false items you need to fill in.
So in this specific case, you can implement this with:
my_flatten([],[]).
my_flatten([X|Xs],RR) :-
my_flatten(Xs,R),
( is_list(X)
-> append(X,R,RR)
; RR = [X | R] ).
or we can flatten recursively with:
my_flatten([],[]).
my_flatten([X|Xs],RR) :-
my_flatten(Xs,R),
( flatten(X, XF)
-> append(XF,R,RR)
; RR = [X | R] ).
Your if_/3 predicate is used for reified predicates.
This worked for me:
myflat([], []).
myflat([H|T], L) :-
myflat(H, L1),
myflat(T, L2),
append(L1, L2, L).
myflat(L, [L]).
I'm trying to rewrite code from Haskell to Prolog.
count :: Eq a => a -> [a] -> Int
count x = length . filter (x==)
f :: [Integer] -> [Integer]
f [] = []
f list = filter (\x -> count x list == 1) list
This code return list that contains elements that appears only once in the list.
So if I have list [1,1,2,2,3,4,4,5] this function returns [3,5]
I tried to find filter construction in Prolog but seems there no such thing. How can I make similar function in Prolog ?
To the existing answers, I would like to add an answer that is quite general in the sense that you can use it in multiple directions.
Building block: list_element_number/3
I start with the following predicate, defining a relation between:
a list Ls0
an element E
the number N of occurrences of E in Ls0
Here it is:
list_element_number(Ls0, E, N) :-
tfilter(=(E), Ls0, Ls),
length(Ls, N).
This solution uses tfilter/3 from library(reif). The predicate subsumes the function count you have posted. The main benefit of this predicate over the function is that the predicate can be used not only in those cases that even Haskell can do easily, such as:
?- list_element_number([a,b,c], a, N).
N = 1.
No, we can use it also in other directions, such as:
?- list_element_number([a,b,c], X, 1).
X = a ;
X = b ;
X = c ;
false.
Or even:
?- list_element_number([a,b,E], X, 2).
E = X, X = a ;
E = X, X = b ;
false.
Or even:
?- list_element_number([A,B,C], X, 3).
A = B, B = C, C = X ;
false.
And even in the most general case, in which all arguments are fresh variables:
?- list_element_number(Ls, E, N).
Ls = [],
N = 0 ;
Ls = [E],
N = 1 ;
Ls = [E, E],
N = 2 ;
Ls = [E, E, E],
N = 3 .
We can fairly enumerate all answers like this:
?- length(Ls, _), list_element_number(Ls, E, N).
Ls = [],
N = 0 ;
Ls = [E],
N = 1 ;
Ls = [_160],
N = 0,
dif(E, _160) ;
Ls = [E, E],
N = 2 .
Main predicate: list_singletons/2
Using this building block, we can define list_singletons/2 as follows:
list_singletons(Ls, Singles) :-
tfilter(count_one(Ls), Ls, Singles).
count_one(Ls, E, T) :-
list_element_number(Ls, E, Num),
cond_t(Num=1, true, T).
This uses cond_t/3 and (again) tfilter/3 from library(reif).
Sample queries
Here are a few sample queries. First, the test case you have posted:
?- list_singletons([1,1,2,2,3,4,4,5], Singles).
Singles = [3, 5].
It works as desired.
Now a case involving variables:
?- list_singletons([A,B], Singles).
A = B,
Singles = [] ;
Singles = [A, B],
dif(A, B).
On backtracking, all possibilities are generated: Either A = B holds, and in that case, there is no element that occurs only once. Or A is different from B, and in that case both A and B occur exactly once.
As a special case of the above query, we can post:
?- list_singletons([A,A], Singles).
Singles = [].
And as a generalization, we can post:
?- length(Ls, _), list_singletons(Ls, Singles).
Ls = Singles, Singles = [] ;
Ls = Singles, Singles = [_7216] ;
Ls = [_7216, _7216],
Singles = [] ;
Ls = Singles, Singles = [_7828, _7834],
dif(_7828, _7834) ;
Ls = [_7216, _7216, _7216],
Singles = [] ;
Ls = [_7910, _7910, _7922],
Singles = [_7922],
dif(_7910, _7922) .
Enjoy the generality of this relation, obtained via logical-purity.
A more simple version :
filter_list(L,OutList):-findall(X, (select(X,L, L1),\+member(X, L1)) , OutList).
?- filter_list([1,1,2,2,3,4,4,5],L).
L = [3, 5].
Without findall, you can try
filter_list(In, Out) :- filter_list(In, _, Out).
filter_list([], [], []).
filter_list([H|T], L1, L2) :-
filter_list(T, LL1, LL2),
( member(H, LL1)
-> L1 = LL1, L2 = LL2
; (select(H, LL2, L2)
-> L1 = [H|LL1]
; L1 = LL1, L2 = [H|LL2])).
without counting...
filter_uniques([],[]).
filter_uniques([H|T],F) :-
delete(T,H,D),
( D=T -> F=[H|R],S=T ; F=R,S=D ),
filter_uniques(S,R).
a more direct rewrite of your code, with library(yall) support for inlining of the filter predicate (the first argument to include/3)
filt_uniq(L,F) :-
include({L}/[E]>>aggregate(count,member(E,L),1),L,F).
A simple version:
(see the comment by #false below, the version with findall/3 has some inconsistency problems in more complex queries but second version looks ok however it is definitely not so efficient ).
filter_list(L,OutList):-findall(X, (member(X,L),count(X,L,N),N=:=1) , OutList).
count(_,[],0).
count(X,[X|T],N):-count(X,T,N1),N is N1+1.
count(X,[X1|T],N):-dif(X,X1),count(X,T,N).
The predicate filter_list/2 uses findall/3 and simply states find all X that belong to the list L and count returns 1 and store them in OutList.
Example:
?- filter_list([1,1,2,2,3,4,4,5],L).
L = [3, 5].
You could write filter_list/2 without using findall/3 like:
filter_list(L,OutList):- filter_list(L,OutList,L).
filter_list([],[],_).
filter_list([H|T],[H|T1],L):-count(H,L,N), N=:=1, filter_list(T,T1,L).
filter_list([H|T],T1,L):-count(H,L,N), N > 1, filter_list(T,T1,L).
I want to create two Prolog relations and place their definitions in a single Prolog file. Define the relations prefix and postfix on lists, meaning that the first argument is a prefix or postfix, respectively, of the second.
?- consult(prepost).
% prepost compiled 0.00 sec, 956 bytes
true.
?- prefix([a,b,c],[a,b,c,e,f]).
true.
?- prefix([a,b,c], [a,b,e,f]).
false.
?- prefix([a,b],[a]).
false.
?- prefix([],[a,b,c,d]).
true.
?- prefix(X,[a,b,c,d]).
X = [] ;
X = [a] ;
X = [a, b] ;
X = [a, b, c] ;
X = [a, b, c, d] ;
false.
?- postfix([n,e],[d,o,n,e]).
true .
?- postfix([],[a,n,y,t,h,i,n,g]).
true .
?- postfix([a,b,c],[a,b,c,d,e]).
false.
?- postfix(X,[a,b,c,d]).
X = [a, b, c, d] ;
X = [b, c, d] ;
X = [c, d] ;
X = [d] ;
X = [] ;
false.
?-
You can use the predicate append/3 to solve your problem in a simple way.
A list of elements is prefix of some other list, if there is a combination where this first list concatenated with another (not relevant), results in your full list.
prefix(Prefix_list, Full_list):- append(Prefix_list, _, Full_list).
You can infer your predicate postfix/2 in the same way:
postfix(Postfix_list, Full_list):- append(_, Postfix_list, Full_list).
Now, put both predicate in a text file, name it prepost.pl, and that's it.