How to inline a goal with findall/3, (use just one predicate)? - list

I have a knowledgebase that looks something like this
fact1(1, _, a, _, _).
fact1(2, _, c, _, _).
fact1(3, _, d, _, _).
fact1(4, _, f, _, _).
fact2(_, 1, b, _, _).
fact2(_, 2, c, _, _).
fact2(_, 4, e, _, _).
For every fact1 & fact2, where (in this example) the numbers match up, I want to have a list of the corresponding letters as tuples.
I would like to use findall/3 and only one predicate for this.
I have asked a question here before on how to solve something similar, where the answer was using two predicates. That solution looked like this:
find_item((Val1,Val2)):-
fact1(A, _, Val1, _, _),
fact2(_, A, Val2, _, _).`
test(Items) :-
findall(Item,find_item(Item),Items).
The result for the given example of facts, should look like this:
[(a, b), (c, c), (f, e)]
Can the two predicates be combined using just findall/3?

You can inline procedure find_item/1 as the goal of findall/3 (use a conjunction of goals instead of a single goal):
test(Items):-
findall((Val1, Val2), (fact1(A, _, Val1, _, _), fact2(_, A, Val2, _, _)), Items).

Related

Prolog - identify sublists of variables

I have an assignment in which I must accomplish the following - given a list such as:
List = [_, _, $, _, _, _, _, $, _, $].
I have to write a predicate pred/2 with pred(List, Sub) such that it is true if and only if Sub is a sublist of size >= 2 with no adjacent variables in which none of the members is a $. Examples:
List = [X, Y, $, P, Q, R, S, $, A, $], pred(List, [X, Y]) should succeed.
List = [X, Y, $, P, Q, R, S, $, A, $], pred(List, [P, Q, R]) should not succeed because S is adjacent to P, Q, R.
List = [X, Y, $, P, Q, R, S, $, A, $], pred(List, [P, Q, R, S]) should succeed.
List = [X, Y, $, P, Q, R, S, $, A, $], pred(List, [A, $]) should not succeed because it includes a $.
I have written the following code, which according to a bit of testing does that I want:
pred(List, Sub) :- pred(List, Sub, []).
pred([], Sub, Sub) :- length(Sub, Len),
Len >= 2.
pred([H|T], Sub, Aux) :- H \== $, !,
append(Aux, [H], New),
pred(T, Sub, New).
pred([$|_], Sub, Sub):- length(Sub, Len), Len >= 2.
pred([$|T], Sub, _) :- pred(T, Sub, []).
However, I feel like this solution is janky. While I am aware StackOverflow is, in general, for things that people can't do, I really feel like I've come up with a rather complicated solution to a simple problem and would like to know of a better way to accomplish what I want.
Thank you in advance!
append/2 is an handy predicate from library(lists). It allows an easier implementation of the requirements:
pred(L, S) :-
append([X, S, Y], L),
length(S, C), C >= 2,
maplist(\==($), S),
( X = [] ; last(X, X_), X_ == $ ),
( Y = [] ; Y = [Y_|_], Y_ == $ ).

Reaching the result then returns to the initial state

So I'm in the process of learning prolog.
All I want to do is to change the order of elements and get the new list as result.
When tracing the solution I get to the right answer, however once I reach the base case Prolog starts to empty the list again and returns and empty list as a result.
Code:
accRev([], [], _) :- !.
accRev([], A, R) :- accRev(A, [], R), !.
accRev([H, H2 |T], A, R):-
append(R, [H], R1),
append(A, [H2], A1),
accRev(T, A1, R1), !.
accRev([H], A, R):-
append(R, [H], R1),
accRev(A, [], R1), !.
accRevT([], [], _) :- !.
accRevT([], A, R) :- accRev(A, [], R), !.
accRevT([H, H2 |T], A, R):-
append(R, [H], R1),
append(A, [H2], A1),
accRevT(T, [H2 | A], [H | R]), !.
accRevT([H], A, R):-
append(R, [H], R1),
accRevT(A, [], [H | R]), !.
Image of the trace
Note how it reaches accRev([], [], [1, 3, 2, 4]) (this is what I would like R to become, R = [1, 3, 2, 4])
So whats wrong?
If understood correctly what you're trying to do you could write:
accRev([],[]).
accRev([X|Y], Lout):-
split_list([X|Y],Z1),
split_list(Y,Z2),
accRev(Z2,Z3),
append(Z1,Z3,Lout).
split_list([],[]).
split_list([X|[]], [X]).
split_list([X,_|T], [X|R]):-split_list(T,R).
Example:
?- accRev([1,2,3,4],R).
R = [1, 3, 2, 4] ;
false.
In your solution I think some problems were caused due to use of too many cuts and accRev([], [], _) as Boris wrote, and also I think you don't need the middle list because for example the clauses:
accRev([], [], _) :- !.
accRev([], A, R) :- accRev(A, [], R), !.
will lead to problems since if the first list is the empty list both clauses match.

Prolog not_member dont do its job

I have a Prolog problem here, I am trying to get unique airports into the list but my predicate does not work as expected.
not_member(C, []).
not_member(C, [H|L]) :-
not_member(C, L),
C \== H.
path(X, Y, [X,Y]) :-
flight(X, Y, _, _, _, _).
path(X, Y, [X,P]) :-
not_member(Z, P),
flight(X, Z, _, _, _, _),
flight(Z, Y, _, _, _, _),
path(Z, Y, P).
Sample query with expected answers:
?- path(dublin, rome, L)
L = [dublin, rome] ;
L = [dublin, paris, rome] ...
If you need facts let me know, your help will be appreciated. Thanks!
The problem is not the (\==)/2. The problem is that an uninstantiated P would make not_member/2 loop. So you need a predicate path/4 with four arguments:
:- use_module(library(basic/lists)).
path(_, X, L, _) :- member(X, L), !, fail.
path(X, X, L, [X|L]).
path(Y, X, L, R) :-
flight(Z, X),
path(Y, Z, [X|L], R).
The above predicate searches from the destination airport backwards, so that we don't need to reverse the resulting list. Here is an example database:
flight(zurich, frankfurt).
flight(frankfurt, zurich).
flight(zurich, munich).
flight(munich, zurich).
flight(munich, frankfurt).
flight(frankfurt, munich).
And here is an example run:
Jekejeke Prolog 2, Runtime Library 1.2.5
(c) 1985-2017, XLOG Technologies GmbH, Switzerland
?- path(zurich, frankfurt, [], L).
L = [zurich,frankfurt] ;
L = [zurich,munich,frankfurt] ;
No

Can anyone help me in writing a prolog program for deleting a list?

Ex: If I have been given two list [1,4,3,2,5,6] and [1,2,3] the final list should be [4,5,6].
i.e., Del([1,4,3,2,5,6], [1,2,3], Result).
----should output Result=[4,5,6].
I have tried something like this:
delete1(A, [A|B], B).
delete1(A, [B, C|D], [B|E]) :- delete1(A, [C|D], E).
But the output I'm getting is by deleting the element being passed as an parameter and not a list.
Output:
delete1(a,[a,b,c,d],Res).
(0) Call: delete1(a,[a,b,c,d],_h210) ?
(0) Exit: delete1(a,[a,b,c,d],[b,c,d]) ?
Res = [b,c,d]
Can anyone please help me how to go about this ?
Pure and simple: Use meta-predicate tfilter/3 in tandem with list_nonmember_t/3!
Like we did with memberd_t/3, we define list_nonmember_t/3 based on if_/3 and (=)/3:
list_nonmember_t([],_,true).
list_nonmember_t([E|Es],X,T) :-
if_(E=X, T=false, list_nonmember_t(Es,X,T)).
Let's put it together!
?- tfilter(list_nonmember_t([1,2,3]), [1,4,3,2,5,6], Xs).
Xs = [4,5,6]. % succeeds deterministically
del1([], _, []).
del1([A|L], B, R) :- member(A, B), del1(L, B, R).
del1([A|L], B, [A|R]) :- not(member(A,B)), del1(L, B, R).
So your delete1 is a good start, it allows to delete single element from the list, but it is not handling all of the cases. For example the lists that are not containing the element to be deleted.
So the right one would be :
delete1(_, [], []).
delete1(A, [A|B], B).
delete1(A, [C|B], [C|E]) :- delete1(A, B, E).
and then using this, you can define your del1 by applying delete1 recursively on the whole list:
del1([], _, []).
del1(L, [], L).
del1(L, [H|T], R) :-
delete1(H, L, R1),
del1(R1, T, R).
And of course you can use builtin list predicates as stated in the other answer.

Prolog substitution

How can I replace a list with another list that contain the variable to be replaced. for example
rep([x, d, e, z, x, z, p], [x=z, z=x, d=c], R).
R = [z, c, e, x, z, x, p]
the x to z and z doesn't change after it has been replaced.
so far I did only the one without the list
rep([], _, []).
rep(L1, H1=H2, L2) :-
rep(L1, H1, H2, L2).
rep([],_,_,[]).
rep([H|T], X1, X2, [X2|L]) :-
H=X1,
rep(T,X1,X2,L),
!.
rep([H|T],X1,X2,[H|L]) :-
rep(T,X1,X2,L).
If you use SWI-Prolog, with module lambda.pl found there : http://www.complang.tuwien.ac.at/ulrich/Prolog-inedit/lambda.pl you can write :
:- use_module(library(lambda)).
rep(L, Rep, New_L) :-
maplist(\X^Y^(member(X=Z, Rep)
-> Y = Z
; Y = X), L, New_L).
You should attempt to keep the code simpler than possible:
rep([], _, []).
rep([X|Xs], Vs, [Y|Ys]) :-
( memberchk(X=V, Vs) -> Y = V ; Y = X ),
rep(Xs, Vs, Ys).
Of course, note the idiomatic way (thru memberchk/2) to check for a variable value.
Still yet a more idiomatic way to do: transforming lists it's a basic building block in several languages, and Prolog is no exception:
rep(Xs, Vs, Ys) :- maplist(repv(Vs), Xs, Ys).
repv(Vs, X, Y) :- memberchk(X=V, Vs) -> Y = V ; Y = X .
Here's how you could proceed using if_/3 and (=)/3.
First, we try to find a single Key in a list of pairs K-V.
An extra argument reifies search success.
pairs_key_firstvalue_t([] ,_ ,_ ,false).
pairs_key_firstvalue_t([K-V|KVs],Key,Value,Truth) :-
if_(K=Key,
(V=Value, Truth=true),
pairs_key_firstvalue_t(KVs,Key,Value,Truth)).
Next, we need to handle "not found" cases:
assoc_key_mapped(Assoc,Key,Value) :-
if_(pairs_key_firstvalue_t(Assoc,Key,Value),
true,
Key=Value).
Last, we put it all together using the meta-predicate maplist/3:
?- maplist(assoc_key_mapped([x-z,z-x,d-c]), [x,d,e,z,a,z,p], Rs).
Rs = [z,c,e,x,a,x,p]. % OK, succeeds deterministically
Let's improve this answer by moving the "recursive part" into meta-predicate find_first_in_t/4:
:- meta_predicate find_first_in_t(2,?,?,?).
find_first_in_t(P_2,X,Xs,Truth) :-
list_first_suchthat_t(Xs,X,P_2,Truth).
list_first_suchthat_t([] ,_, _ ,false).
list_first_suchthat_t([E|Es],X,P_2,Truth) :-
if_(call(P_2,E),
(E=X,Truth=true),
list_first_suchthat_t(Es,X,P_2,Truth)).
To fill in the "missing bits and pieces", we define key_pair_t/3:
key_pair_t(Key,K-_,Truth) :-
=(Key,K,Truth).
Based on find_first_in_t/4 and key_pair_t/3, we can write assoc_key_mapped/3 like this:
assoc_key_mapped(Assoc,Key,Value) :-
if_(find_first_in_t(key_pair_t(Key),_-Value,Assoc),
true,
Key=Value).
So, does the OP's use-case still work?
?- maplist(assoc_key_mapped([x-z,z-x,d-c]), [x,d,e,z,a,z,p], Rs).
Rs = [z,c,e,x,a,x,p]. % OK. same result as before
Building on find_first_in_t/4
memberd_t(X,Xs,Truth) :- % memberd_t/3
find_first_in_t(=(X),_,Xs,Truth).
:- meta_predicate exists_in_t(2,?,?). % exists_in_t/3
exists_in_t(P_2,Xs,Truth) :-
find_first_in_t(P_2,_,Xs,Truth).
I find your code rather confused. For one thing, you have rep/3 and rep/4, but none of them have a list in the second position where you're passing the list of variable bindings. H1=H2 cannot possibly match a list, and that's the only rep/3 clause that examines the second argument. If this is a class assignment, it looks like you're a little bit behind and I'd suggest you spend some time on the previous material.
The solution is simpler than you'd think:
rep([], _, []).
rep([X|Xs], Vars, [Y|Rest]) :- member(X=Y, Vars), rep(Xs, Vars, Rest).
rep([X|Xs], Vars, [X|Rest]) :- \+ member(X=_, Vars), rep(Xs, Vars, Rest).
We're using member/2 to find a "variable binding" in the list (in quotes because these are atoms and not true Prolog variables). If it's in the list, Y is the replacement, otherwise we keep using X. And you see this has the desired effect:
?- rep([x, d, e, z, x, z, p], [x=z, z=x, d=c], R).
R = [z, c, e, x, z, x, p] ;
false.
This could be made somewhat more efficient using "or" directly (and save us a choice point):
rep([], _, []).
rep([X|Xs], Vars, [Y|Ys]) :-
(member(X=Y, Vars), ! ; X=Y),
rep(Xs, Vars, Ys).
See:
?- rep([x, d, e, z, x, z, p], [x=z, z=x, d=c], R).
R = [z, c, e, x, z, x, p].