Prolog - identify sublists of variables - list

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_ == $ ).

Related

A list on which there are duplicates(on sequence),a part of which is needed to be transferred

This question is about a type of custom "cryptography" on which a word has duplicate letters in sequence. After the duplicate follows a part that is misplaced and needs to end up at the back of the list. My rule has the structure as: crypto(Word1,Word2) where the Word1 is a crypto and Word2 is uncrypted/answer
changed some code as i found something closer to the answer
What do i want :
?- crypto([h,a,a,o,m,e,n,n,d,s], L).
L = [h,a,n,d,s,o,m,e]
What do i get if(1 duplicate) :
?- crypto([h,a,a,o,m,e,n,d,s], L).
L = [h,a,n,d,s,o,m,e]
I tried the following code:
crypto([],[]).
crypto([H|T], List) :- %is true when its duplicate
member(H, T),
append(Empty_at_first,[Arxh|Telos],[H|T]),
append(A,[Ar|Te],Telos),
ml(Te,3,Result_ml),
append([H],Result_ml,Result),
crypto(Result,List).
crypto([H| T], [H|T1]) :-
not(member(H, T)),
crypto(T, T1).
ml([H|T],R):-append(T,[H|[]],R).
ml(A,0,A).
ml([H|T],N,R):-
N1 is N - 1, ml([H|T],R1), ml(R1,N1,R),!.
The specification is less clear than it should be, but here is my understanding: An encrypted sequence consists of the following, in order:
some prefix
a duplicated delimiter indicating the start of the displaced sequence
the displaced "final" sequence itself
a duplicated delimiter indicating the end of the displaced sequence
a suffix
A decrypted sequence has the same parts in a different order:
the prefix
the "start" delimiter (only once)
the "end" delimiter (only once)
the suffix
the "final" part
A useful strategy to solve this problem is to write separate predicates for identifying the parts of an encrypted sequence, and for putting those parts back together to get a decrypted sequence. Generally, in Prolog (even more than in other programming languages) you should try hard to recognize distinct sub-problems and write separate predicates for dealing with them.
For example:
encrypted_parts(Encrypted, Prefix, X, Y, Suffix, Final) :-
append(Prefix, L1, Encrypted),
L1 = [X, X | L2],
append(Final, L3, L2),
L3 = [Y, Y | L4],
Suffix = L4.
This behaves like this:
?- encrypted_parts([h,a,a,o,m,e,n,n,d,s], Prefix, X, Y, Suffix, Final).
Prefix = [h],
X = a,
Y = n,
Suffix = [d, s],
Final = [o, m, e] ;
false.
It recognizes that a and n are the duplicated delimiters and that [o, m, e] is the sequence in between, which should end up at the end of the decrypted sequence.
Putting these parts back together is very similar:
decrypted_parts(Decrypted, Prefix, X, Y, Suffix, Final) :-
append(Prefix, L1, Decrypted),
L1 = [X, Y | L2],
append(Suffix, L3, L2),
Final = L3.
And finally:
crypto(Encrypted, Decrypted) :-
encrypted_parts(Encrypted, Prefix, X, Y, Suffix, Final),
decrypted_parts(Decrypted, Prefix, X, Y, Suffix, Final).
?- crypto([h,a,a,o,m,e,n,n,d,s], Decrypted).
Decrypted = [h, a, n, d, s, o, m, e] ;
false.
All of this is more readable with DCGs, if you know about them already:
encrypted(Prefix, X, Y, Suffix, Final) -->
list(Prefix),
list([X, X]),
list(Final),
list([Y, Y]),
list(Suffix).
decrypted(Prefix, X, Y, Suffix, Final) -->
list(Prefix),
[X],
[Y],
list(Suffix),
list(Final).
list([]) -->
[].
list([X | Xs]) -->
[X],
list(Xs).
crypto_dcg(Encrypted, Decrypted) :-
phrase(encrypted(Prefix, X, Y, Suffix, Final), Encrypted),
phrase(decrypted(Prefix, X, Y, Suffix, Final), Decrypted).

Using Prolog to process a list

I have a problem in Prolog.
Consider a list where each element is referring to a position/location in the same list.
I want to write a program which returns true if I start with the same location that I end in. For example:
If I give it find-s([3,2,0,1],0)
It starts with 0 index which contains 3
then goes to the 3 index which contains 1
then the 1 index to find 2
finally to index 2 which contains 0 !
so it returns true in this case
I tried this but it didn't work
position([Head1| Tail1], Var1) :-
( Head1 == Var1,
Tail1 == Var1 ->
true
).
this should work
find_s(L, S) :- nth0(S, L, I), find_s(L, I, S).
find_s(_, S, S).
find_s(L, I, S) :- nth0(I, L, J), find_s(L, J, S).
but I think it could easily loop. So, instead of nth0/3, let's use nth0/4, after this consideration:
?- nth0(2,[a,b,c,d],X,Y).
X = c,
Y = [a, b, d].
?- nth0(2,L,$X,$Y).
L = [a, b, c, d].
for sake of readability, let's introduce a replace_nth0/4
replace_nth0(P, L, E, LrepE) :-
nth0(P, L, E, L_Without_E),
nth0(P, LrepE, -1, L_Without_E).
and then
find_s(L, S) :- nth0(S, L, I), find_s(L, I, S).
find_s(_, S, S).
find_s(L, I, S) :- I >= 0, replace_nth0(I, L, J, L_rep), find_s(L_rep, J, S).

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

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].

Prolog - Filter List

So I have this homework due tommorow. I have to filter every nth element of a list and return it as a list. So for example:
?- everyNth(3,[a,b,c,d,e,f],Rs).
Rs = [c,f].
My Idea was basically:
everynth(N, [X|Xs], L) :- everynth(N, [X|Xs], N, L).
everynth(N, [], C, L).
everynth(N, [X|Xs], 0, [X]) :- everynth(N, Xs, N, [X]).
everynth(N, [X|Xs], C, L) :- C1 is C -1,
everynth(N,Xs,C1,L).
But it does not work this way, because in the third row it tries to match X and the return X and the Count 0 the second time it goes there.
You are almost there. Check these modifications:
everynth(N, L, NL) :- everynth(N, L, N, NL).
everynth(_, [], _, []).
everynth(N, [X|Xs], 1, [X|NXs]) :- everynth(N, Xs, N, NXs).
everynth(N, [_|Xs], C, NXs) :- C1 is C-1, C1>0,
everynth(N,Xs,C1,NXs).
The first clause of everynth/4 is the termination of the recursion. It should give an empty list when there are no more items in the input list.
The second clause of everynth/4 deals with the nth item, it has to put the input item in the output list and keep processing the remaining items restarting your item counter.
And the third clause of everynth/4 deals with items which are not the nth element, so you have to skip the item, decrement the counter and continue with the remaining items.
everynth(_, _, [], R, R).
everynth(1, M, [X|Xs], Z, R) :- append(Z, [X], Z1), everynth(M, M, Xs, Z1, R).
everynth(N, M, [_|Xs], Z, R) :- N > 1, N1 is N - 1, everynth(N1, M, Xs, Z, R).
?- everynth(3, 3, [a,b,c,d,e,f], [], Rs).
Rs = [c, f] .