Replace portions of a list with another in Prolog - replace

I want to replace b,c with x,y,z in a list in Prolog. I have a list [a,b,c,d,e,f] and result will be [a,x,y,z,d,e,f]. How can I write this in Prolog?
replace([],_,[]).
replace([x|T1],Var,[Y|T2]):-
member(X=Y,var),
!
; X=Y
),
replace(T1,Var,T2).
-? replace([a,b,c,d,e,f],[b,c=x,y,z],R).

This is essentially equivalent to the problem of replacing substrings in Prolog. Using the replace_substring/4 predicate, you can replace a subsequence of the list in this way:
:- initialization(main).
:- set_prolog_flag(double_quotes, chars).
main :-
replace_substring([a,b,c,d,e,f],[b,c],[x,y,z],Result),
writeln(Result).
replace_substring(String, To_Replace, Replace_With, Result) :-
append([Front, To_Replace, Back], String),
append([Front, Replace_With, Back], Result).
This program prints [a,x,y,z,d,e,f].

Related

Prolog - first list is sublist of second list while maintaining order?

I want to check if elements of list L1 occur consecutively, and in the same order, in list L2.
For example - check([b,c],[a,b,c,d]) must return true while check([b,d],[a,b,c,d]) must return false
I looked at similar posts Prolog - first list is sublist of second list? and also tried out similar solutions but whenever i try to check if elements are present, i am unable to check if ordering is consecutive
check( [], _ ).
check( [X|XS], [X|XSS] ) :- sublist( XS, XSS ).
check( [X|XS], [_|XSS] ) :- sublist( [X|XS], XSS ).
and if i try to check if ordering is correct then my code is breaking.
check( [], _ ).
check( [X|XS], [X|XSS] ) :- sublist( XS, XSS ).
Interesting problem! I'm surprised at how much code it took, so there may be a better solution than this.
First we need a helper to insist that a list is a prefix of another list. The base case is that we ran out of a prefix list; the inductive case is that the current items match and the remainder of both lists is a prefix match.
prefix([X|Xs], [X|Ys]) :- prefix(Xs, Ys).
prefix([], _).
Now finding a consecutive sublist amounts to searching down a list for prefix matches. If the current items match, then having a prefix is a match:
consecutive_sublist([X|Xs], [X|Ys]) :- prefix(Xs, Ys).
Otherwise, we just discard this element of the search target and try again on the sublist:
consecutive_sublist(Prefix, [_|Ys]) :- consecutive_sublist(Prefix, Ys).
We can make use of append/2 [swi-doc] to write this with a one-liner:
subsequence(X, Y) :-
append([_,X,_], Y).
or we can implement a subsequence/4 that will unify two variables Prefix and Suffix with the list before and after the subsequence:
subsequence(X, Y, Prefix, Suffix) :-
append([Prefix,X,Suffix], Y).
Here we thus have two don't care variables that will collect the prefix and suffix before and after the subsequence.
An alternative solution using the de facto standard definition of the append/3 predicate:
check(SubList, List) :-
append(Prefix, _, List),
append(_, SubList, Prefix).
Sample calls:
| ?- check([b,d],[a,b,c,d]).
no
| ?- check([b,c],[a,b,c,d]).
true ? ;
no
| ?- check([b,c],[a,b,c,d,b,c,f]).
true ? ;
true ? ;
no
We can also use this definition to generate sublist-list pairs:
| ?- check(SubList, List).
SubList = [] ? ;
List = [A|_]
SubList = [A] ? ;
List = [_|_]
SubList = [] ? ;
List = [A,B|_]
SubList = [A,B] ? ;
List = [_,A|_]
SubList = [A] ? ;
List = [_,_|_]
SubList = [] ? ;
List = [A,B,C|_]
SubList = [A,B,C] ? ;
List = [_,A,B|_]
SubList = [A,B] ? ;
...
This problem also gives you the opportunity to learn about termination properties of predicates. As an experiment, exchange the order of the append/3 calls and then check what happens on backtracking for e.g. the two first sample calls.

Replacing substrings in Prolog [duplicate]

This question already has answers here:
How do you do a search and replace of a list with another sublist in Prolog?
(2 answers)
Closed 4 years ago.
In order to replace a substring in a string, I wrote a predicate called replace_substring. It uses SWI-Prolog's append/3 predicate:
:- initialization(main).
:- set_prolog_flag(double_quotes, chars).
main :-
replace_substring("this is a string","string","replaced string",Result),
writeln(Result).
replace_substring(String,To_Replace,Replace_With,Result) :-
append(First,To_Replace,String),
append(First,Replace_With,Result).
Still, I'm not sure if this is the most efficient way to replace substrings in Prolog. Does Prolog have a built-in predicate that could be used for the same purpose?
The short answer is, no, Prolog does not have a built-in string replace predicate. What you show will only replace the substring if that substring is at the end of the original string. That is, it will replace "abc" in the string "xyzabc" but not in the string "xyabcz".
You can use append/2:
replace_substring(String, To_Replace, Replace_With, Result) :-
append([Front, To_Replace, Back], String),
append([Front, Replace_With, Back], Result).
If you want it to succeed without replacing on a non-match, then:
replace_substring(String, To_Replace, Replace_With, Result) :-
( append([Front, To_Replace, Back], String)
-> append([Front, Replace_With, Back], Result)
; Result = String
).
As #false hints in his question, do you want to handle replacing multiple occurrences? If so, the extension to your method would be:
replace_substring(String, To_Replace, Replace_With, Result) :-
( append([Front, To_Replace, Back], String)
-> append([Front, Replace_With, Back], R),
replace_substring(Back, To_Replace, Replace_With, Result)
; Result = String
).

Prolog insert integer after every non integer element

I have the list [r,s,2,t,3,u,v] and I need to make a list that will look like [r,5,s,2,t,3,u,5,v,5]. The rule is: for every non integer that is not followed by an integer, a 5 will be added after that element.
I am new to Prolog and this is my code so far:
insertInL([],[]).
insertInL([F,S|Tail], [F,X|Rest]) :-
integer(S), X = S, insertInL(Tail, Rest).
I know there should be one case where S is not an integer but I don't know hot to treat it.
Edit:
I renewed my code:
insertInL([],[]).
insertInL([F,S|T1], [F,S|T2]) :-
integer(S), insertInL(T1, T2).
insertInL([F,S|T1], [F,1|T2]) :-
\+ integer(S), insertInL([S|T1], T2).
Now it does fine unless I have a non integer as last element.
Edit2:
Now it works properly.
insertInL([],[]).
insertInL([F],[F,1]) :-
\+ integer(F).
insertInL([F,S|T1], [F,S|T2]) :-
integer(S), insertInL(T1, T2),!.
insertInL([F,S|T1], [F,1|T2]) :-
\+ integer(S), insertInL([S|T1], T2).
Here's how you could do it while preserving logical-purity!
Based on if_/3 and integer_t/2 we define:
list_fived([], []).
list_fived([X|Xs], [X|Ys]) :-
if_(integer_t(X),
list_fived(Xs, Ys),
past_nonint(Xs, Ys)).
past_nonint([], [5]).
past_nonint([X|Xs], Ys0) :-
if_(integer_t(X),
(Ys0 = [X|Ys], list_fived(Xs, Ys)),
(Ys0 = [5|Ys], list_fived([X|Xs], Ys))).
Sample query using SICStus Prolog 4.3.2:
| ?- list_fived([r,s,2,t,3,u,v], Xs).
Xs = [r,5,s,2,t,3,u,5,v,5] ? ; % expected result as given by the OP
no

compare the elements of one list with the elements of another list in prolog

First of all, I am completely a beginner at Prolog.
I am trying to compare each element of a list with each element of another list. By compare, I mean sending these 2 elements to a predicate(conflicts) I wrote. So far, I got this:
%iterate over the first list
cmp_list([],_,_).
cmp_list([X|Y],[A|B],Result):-
cmp_list_inner(X,[A|B],Result),
cmp_list(Y,[A|B],Result).
%iterate over the second list
cmp_list_inner(_,[],_).
cmp_list_inner(X,[A|B],S):-
not(conflicts(X,A)), %compare
cmp_list_inner(X,B,[X-A|S]).%if okay, add their combination, i.e X-A to the main list which will be returned
The predicate cmp_list stands for the recursion of outer list, whereas the one with inner stands for inner list.
cmp_list(firstlist, secondlist, new list after the combination which will be returned.)
This doesn't work! Even though it adds the values for a single element in the first value to the main list, it doesn't append the second comparison(for the second element in the first list) to the main list which will be returned. Result should be in the form of:
[X1-Y1], [X1-Y2], [X2-Y1], [X2-Y2].... where Xs are from the first list and Ys are from the second list.
Any help would be appreciated. Thanks!
You only need one simple predicate:
cmp_list([], [], []). % cmp_list succeeds for two empty lists
cmp_list([H1|T1], [H2|T2], [H1-H2|R]) :-
% cmp_list of [H1|T1] and [H2|T2] succeeds if...
\+ conflicts(H1, H2), % conflicts fails for H1 and H2
cmp_list(T1, T2, R). % cmp_list succeeds for T1 and T2, result R
% Otherwise, cmp_list fails (e.g., lists are diff length)
A little more compact would be to use the built-in predicate, maplist:
cmp_list(L1, L2, R) :- % cmp_list succeeds on L1, L2 if...
maplist(no_conflicts, L1, L2, R). % no_conflicts succeeds for every
% corresponding pair of L1, L2 elements
no_conflicts(X, Y, X-Y) :- \+ conflicts(X-Y).
If you just want to capture all the corresponding pairs that don't conflict and ignore the rest, then:
cmp_list([], _, []).
cmp_list([_|_], [], []).
cmp_list([H1|T1], [H2|T2], R) :-
( conflicts(H1, H2)
-> cmp_list(T1, T2, R)
; R = [H1-H2|R1],
cmp_list(T1, T2, R1)
).
This uses the "if-then-else" pattern in Prolog formed by grouping -> with ;. This will create a result that looks like, [x1-y1, x3-y3, ...]. You can choose however you want to form your result elements by changing this line:
R = [H1-H2|R1]
For example, R = [[H1,H2]|R1] would yield a result that looks like, [[x1,y1], [x3,y3], ...].
For the more general problem (i.e., the one you were really looking for :)), I'll start with the original code but modify it where it's needed:
%iterate over the first list
cmp_list([], _, []).
cmp_list([X|T], L2, Result):-
cmp_list_inner(X, L2, R1),
cmp_list(T, L2, R2),
append(R1, R2, Result).
%iterate over the second list
cmp_list_inner(_, [], []).
cmp_list_inner(X, [A|B], R) :-
( conflicts(X, A)
-> cmp_list_inner(X, B, R)
; R = [X-A|T],
cmp_list_inner(X, B, T)
).

RegEx Parser written in Prolog

I've been banging my head against the wall on this homework problem for a few hours now. We have to parse a regular expression with Prolog. For the most part, the predicates I have work, but there's a few regular expression and string combos which cause them to run out of stack space in SWI-Prolog. Here's a sample with two of the Regex string combinations, one that works and one that doesn't:
star(star(char(a))), []
star(star(char(a))), [a]
The first one works and the second one runs out of stack.
Here's the predicates I'm using:
re_match(epsilon, []).
re_match(char(Letter), [Letter]).
re_match(star(_), []).
re_match(seq(Rx1, Rx2), List) :- append(List1, List2, List), re_match(Rx2, List2), re_match(Rx1, List1).
re_match(alt(Rx1, Rx2), List) :- re_match(Rx1, List); re_match(Rx2, List).
re_match(star(Rx), List) :- append(List1, List2, List), re_match(Rx, List1), re_match(star(Rx), List2).
I'm not sure what change I need to make to get it to work right, but I'm not sure what else to do.
Also, changing List :- append(List1, List2, List) to [H|T] does not evaluate to true for one of the examples.
Consider using DCG notation for better readability and to more easily reason about termination properties:
:- op(100, xf, *).
rexp(eps) --> [].
rexp([T]) --> [T].
rexp(_*) --> [].
rexp(R*) --> rexp(R), rexp(R*).
rexp(s(R1,R2)) --> rexp(R1), rexp(R2).
rexp((R1|R2)) --> ( rexp(R1) ; rexp(R2) ).
Example using length/2 to generate increasingly longer lists to generate strings that are matched by the regexp:
?- length(Ls, _), phrase(rexp(s(([a]|[b]),[c]*)), Ls).
Ls = [a] ;
Ls = [b] ;
Ls = [a, c] ;
Ls = [b, c] ;
Ls = [a, c, c] ;
etc.
I don't have access to SWI Prolog right now, but here is a guess:
Try changing
re_match(star(Rx), List) :- append(List1, List2, List),
re_match(Rx, List1),
re_match(star(Rx), List2).
to
re_match(star(Rx), List) :- append([H|List1], List2, List),
re_match(Rx, [H|List1]),
re_match(star(Rx), List2).
to force re_match to "eat something" when it iterates on the star construct.