Remove Item from List of Lists - prolog

I have a list containing records which have an Id, name and gender. I am trying to have a user pass in either the Id (an integer) or name (a string), and removing that record, but it's not doing anything.
This is what i have:
choice(5,X) :-
write('\tEnter an ID or Name:'),
read(Item),
rem(Item, X, X2),
menu(X2).
and my remove is:
rem(Item, [], []) :- write("List is empty.").
rem(Item, [[Item,RT]|L],L).
rem(Item, [[ID, Item|PT]|L]|L).
rem(Item, [X|XT],[X|YT]) :- rem(Item, XT, YT).
But when i run it, it just gives me a list of integers.
Any help would be much appreciated!

May be I misunderstood the problem, but I think that
rem(Item, L_In, L_Out) :-
select([Item|_], L_In,L_Out);select([_,Item|_], L_In, L_Out).
does the job.

Your code is (almost) working fine.
You just had a typo in the third rule: you wrote a | instead of a ,, and the prolog interpreter lets you know with:
Warning: user://1:18:
Clauses of (rem)/3 are not together in the source-file
You may want to remove the write("List is empty."), because that rule non only matches when you try to remove an item from an empty list, but it is also the base case of the recursive delete. It prints numbers because you supply it with a string ("abc") instead of an atom ('abc'). Compare the result of write('abc') with the result of write("abc").
Also, you need a cut in the removing rule, because if the item exists in the list, you want to remove it, and you are not interested in other solution arising from backtracking from that choice point.
rem(Item, [], []).
rem(Item, [[Item,RT]|L],L) :- !.
rem(Item, [[ID, Item|PT]|L],L) :- !.
rem(Item, [X|XT],[X|YT]) :- rem(Item, XT, YT).
Test:
?- rem(x, [[a,3],[b,4],[remove_me,x,6],[d,8]], X).
X = [[a, 3], [b, 4], [d, 8]].

Related

Compare and remove elements from list

So i want to make a program that, given a list and an element, returns only the list until said element appears,like this:
propaga( [(1,1),(1,2),(1,3),(1,4)],(1,3),L).
L = [(1,1),(1,2),(1,3)].
Currently i have this:
adiciona((X,Y),[],[(X,Y)]).
adiciona((X,Y), [(W,Z)|Tail],[(W,Z)|Tail1]):-
adiciona((X,Y),Tail,Tail1).
propaga_aux([X|_], X, [X]).
propaga_aux([(X,Y)|_], (Z,W), P):-
(X,Y) = (Z,W), !,
adiciona((X,Y),[],P).
propaga_aux([(X,Y)|T], (Z,W), P):-
(X,Y) \= (Z,W), !,
adiciona((X,Y),[],P),
propaga_aux(T, (Z,W), P).
Adiciona adds an element to the end of a list.
It keeps returning false, i think the problem is how i use the predicate adiciona but i can't figure out what i'm doing wrong, i have tried a lot of variations and i can't get this right, can someone help me?
Your predicate adiciona works correctly.
However note that you don't necessarily need to repeat the term (A,B) everywhere. If you put a single variable in place of that it will work the same way, and it will be more generic.
The problem is the other predicate: I would make it this way:
when you haven't found the matching item yet, the items are copied to the output list recursively;
when the matching item is found, the output list is composed only of that element, and recursion will stop.
At this point you realize that the predicate adiciona is not needed to solve this problem.
Prolog code:
propaga([X|Xs], Z, [X|Ys]) :- X \= Z, propaga(Xs, Z, Ys).
propaga([X|_], X, [X]).
Test:
?- propaga( [(1,1),(1,2),(1,3),(1,4)],(1,3),L).
L = [(1, 1), (1, 2), (1, 3)] ;
false.

Adding to a list of lists in Prolog

I am currently attempting to write a Prolog program which will add a given character to the end of a list. The list's I want to append are elements within a list. This is what I currently have.
extends(X, [], []).
extends(X, [[Head]|Lists], Y):-
append([X], [Head], Y),
extends(X, Lists, [Y]).
Here I'm attempting to concatenate X and Head, storing it in Y. However I want Y to be a list of lists, so when it repeats the process again the next concatenation will be stored also in Y. So at the end of the program Y would store the results of all the concatenations. I would want the result to look like as follows.
?- extends(a, [[b,c], [d,e,f], [x,y,z]], Y).
Y = [[b,c,a], [d,e,f,a], [x,y,z,a]].
Could anyone help me out with this?
You want to apply some operation to corresponding elements of two lists. That operation talks about lists itself. It's easy to get confused with the nested levels of lists, so let's try not to think in those terms. Instead, define first a predicate that does the extension of one list:
element_list_extended(Element, List, Extended) :-
append(List, [Element], Extended).
This behaves as follows, using cases from your example:
?- element_list_extended(a, [b, c], Extended).
Extended = [b, c, a].
?- element_list_extended(a, List, [x, y, z, a]).
List = [x, y, z] ;
false.
Looks good so far. All we need to do is to apply this operation to corresponding elements of two lists:
extends(_Element, [], []).
extends(Element, [Xs | Xss], [Ys | Yss]) :-
element_list_extended(Element, Xs, Ys),
extends(Element, Xss, Yss).
And this works:
?- extends(a, [[b,c], [d,e,f], [x,y,z]], Y).
Y = [[b, c, a], [d, e, f, a], [x, y, z, a]] ;
false.
The key to making it work was to decompose the problem into two parts and to solve those simpler parts separately.
Now, if we like, since the definition of element_list_extended/3 is a single clause containing a single goal, we might decide to do without it and inline its definition into extends/3:
extends(_Element, [], []).
extends(Element, [Xs | Xss], [Ys | Yss]) :-
append(Xs, [Element], Ys),
extends(Element, Xss, Yss).
As you can see, you were quite close! You just had some superfluous brackets because you got confused about list nesting. That's precisely where decomposing the problem helps.
(As the other answer said, SWI-Prolog has some useful libraries that allow you to express even this in even shorter code.)
extends(PostFix, ListIn, ListOut) :-
maplist({PostFix}/[In,Out]>>append(In,[PostFix],Out),ListIn, ListOut).
This is using library(yall) a maplist/3 and append/3.

Replace atom with variable

I have a term which may or may not contain the atom 'this'. The term may also contain variables.
I need to replace 'this' with a variable I. How can I do this?
I tried to do something like this:
term_to_atom((f(a), g(this, b), ...), A),
tokenize_atom(A, L),
replace(this, I, L, L2)
It seemed to work. The problem is, I need to go back to the original term and I can't do it...
SWI-Prolog has atomic_list_concat/2 and atom_to_term/2 which should help you go back to the original term.
main :-
term_to_atom((f(a), g(this, b)), A),
tokenize_atom(A, L),
replace(this, 'I', L, L2),
atomic_list_concat(L2, A2),
atom_to_term(A2, T, []),
writeln(T).
?- main.
f(a),g(_G69,b)
true .
Take a look at this predicate (replace/4):
replace(Term,Term,With,With) :-
!.
replace(Term,Find,Replacement,Result) :-
Term =.. [Functor|Args],
replace_args(Args,Find,Replacement,ReplacedArgs),
Result =.. [Functor|ReplacedArgs].
replace_args([],_,_,[]).
replace_args([Arg|Rest],Find,Replacement,[ReplacedArg|ReplacedRest]) :-
replace(Arg,Find,Replacement,ReplacedArg),
replace_args(Rest,Find,Replacement,ReplacedRest).
An example of what you need:
| ?- replace(f(1,23,h(5,this)),this,Var,Result).
Result = f(1,23,h(5,Var))
yes

Prolog Subtract List Unification

I am trying to subtract one list from another in prolog. In my program the input list have blank spaces in them (e.g. [1,2,_,4])
I am getting the following output:
?- subtract([1,2,3,4],[3,4,_],L).
L = [2].
when I want my output to be
L = [1,2].
So my question is how can I prevent the blank spaces from unifying with other elements? Have been stuck on this for a while.
Assuming you want the "blank spaces" to be ignored, you can simply make a version of each list with those removed and compute their difference:
listWOblanks( [], [] ).
listWOblanks( [H|T], Tx ) :- var(H), !, listWOblanks( T, Tx ).
listWOblanks( [H|T], [H|Tx] ) :- listWOblanks( T, Tx ).
If, when the first list has a blank and the second does not, you need the result to still have a blank, you could modify the above to add a 3rd argument that tells you if any blanks were removed so you can correct the difference accordingly. I believe SWI-Prolog has a predicate, ground, which will tell you if a term has no variables in it, which would do the job w/o needing to modify listWOblanks.
larsmans is correct, the _ is an anonymous variable, and the definition of lists:subtract/3 (which I'm assuming you're using in SWI-Prolog) will always unify them to ground list members because of it's definition using memberchk/2.
If you want subtract behaviour where variables are to be treated like ground terms, then you can redefine it like this:
subtract2([], _, []) :- !.
subtract2([A|C], B, D) :-
var_memberchk(A, B), !,
subtract2(C, B, D).
subtract2([A|B], C, [A|D]) :-
subtract2(B, C, D).
Note that subtract2/3 here is nearly the same as the definition of lists:subtract/3 (try listing(subtract). to see for yourself). The only difference is the list membership predicate, var_memberchk/2, which is defined like this:
var_memberchk(A0, [A1|_]) :-
A0 == A1, !.
var_memberchk(A0, [_|R]) :-
var_memberchk(A0, R).
This checks to see if a variable, atom or term is in the list. So, trying this we get:
?- subtract2([1,2,3,4],[3,4,_],L).
L = [1, 2].
Note that it still works if we name the variables, as you'd expect:
?- subtract2([1,2,A,3,B,4],[3,A,4],L).
L = [1, 2, B].
It also works if we explicitly give names to anonymous variables, like this:
?- subtract2([1,2,_A,3,_B,4],[3,_A,4],L).
L = [1, 2, _B].
Finally, note that since _ doesn't have a name, subtract2/3 will never be able to match it to other anonymous variables in either list, for example:
subtract2([1,2,_,4],[3,_,4],L).
L = [1, 2, _G415].
Where _G415 is the anonymous global variable denoted by the _ in the first input list. The second is a different global variable (like _G416, for instance), so could never match the anonymous variable in the first list.
Another way:
% Uses list catenation to generate sublists /subtraction
conc([], L, L).
conc([X|L1], L2, [X|L3]) :-
conc(L1, L2, L3).
% Finds all list members that have values and then
% use list catenation to generate the sublists
subtract(L1, L2, L3) :-
findall(D, (nth0(N, L2, D), nonvar(D)), PureL2),
conc(L3, PureL2, L1).
This assumes that only one list has '_', but you could do the same findall for L1 if both lists have the same problem.

Prolog difference routine

I need some help with a routine that I am trying to create. I need to make a routine that will look something like this:
difference([(a,b),(a,c),(b,c),(d,e)],[(a,_)],X).
X = [(b,c),(d,e)].
I really need help on this one..
I have written a method so far that can remove the first occurrence that it finds.. however I need it to remove all occurrences. Here is what I have so far...
memberOf(A, [A|_]).
memberOf(A, [_|B]) :-
memberOf(A, B).
mapdiff([], _, []) :- !.
mapdiff([A|C], B, D) :-
memberOf(A, B), !,
mapdiff(C, B, D).
mapdiff([A|B], C, [A|D]) :-
mapdiff(B, C, D).
I have taken this code from listing(subtract).
I don't fully understand what it does, however I know it's almost what I want. I didn't use subtract because my final code has to be compatible with WIN-Prolog... I am testing it on SWI Prolog.
Tricky one! humble coffee has the right idea. Here's a fancy solution using double negation:
difference([], _, []).
difference([E|Es], DL, Res) :-
\+ \+ member(E, DL), !,
difference(Es, DL, Res).
difference([E|Es], DL, [E|Res]) :-
difference(Es, DL, Res).
Works on SWI-PROLOG. Explanation:
Clause 1: Base case. Nothing to diff against!
Clause 2: If E is in the difference list DL, the member/2 subgoal evaluates to true, but we don't want to accept the bindings that member/2 makes between variables present in terms in either list, as we'd like, for example, the variable in the term (a,_) to be reusable across other terms, and not bound to the first solution. So, the 1st \+ removes the variable bindings created by a successful evaluation of member/2, and the second \+ reverses the evaluation state to true, as required. The cut occurs after the check, excluding the 3rd clause, and throwing away the unifiable element.
Clause 3: Keep any element not unifiable across both lists.
I am not sure, but something like this could work. You can use findall to find all elements which can't be unified with the pattern:
?- findall(X, (member(X, [(a,b),(b,c),(a,c)]), X \= (a,_)), Res).
gets the reply
Res = [ (b, c) ]
So
removeAll(Pattern, List, Result) :-
findall(ZZ109, (member(ZZ109, List), ZZ109 \= Pattern), Result).
should work, assuming ZZ109 isn't a variable in Pattern (I don't know a way to get a fresh variable for this, unfortunately. There may be a non-portable one in WIN-Prolog). And then difference can be defined recursively:
difference(List, [], List).
difference(List, [Pattern|Patterns], Result) :-
removeAll(Pattern, List, Result1),
difference(Result1, Patterns, Result).
Your code can be easily modified to work by making it so that the memberOF predicate just checks to see that there is an element in the list that can be unified without actually unifying it. In SWI Prolog this can be done this way:
memberOf(A, [B|_]) :- unifiable(A,B,_).
But I'm not familiar with WIN-PRolog so don't know whether it has a predicate or operator which only tests whether arguments can be unified.

Resources