Here's a code which recursively add the element X at the end of the list.
app(X, [], [X]).
app(X, [Y | S], [Y | S2]) :- app(X, S, S2).
Could anyone explain me how it works? Where's the return statement, what exactly the app(X, S, S2) [Y | S], [Y | S2] do?
You don't need return statement everything is done by unification (simply pattern matching). The clause:
app(X, [Y | S], [Y | S2])
states that the second argument is a list with head Y and tail S and the third argument is a list with head Y and tail S2. So it forces (by using unification) the heads of the two lists to be the same. Recursively the two lists become identical except the fact that the third argument list has one more element in the end (element X) and this is defined by the first clause. Note that second clause only works for lists with one or more elements. So as a base of the recursion when we examine the empty list (in the second parameter) then the third list due to first clause contains only one more element the element X.
Prolog programs are made by defining facts and rules. You define facts and rules, and Prolog interpreter tries to come up with solutions to make them true. Other than this basic concept, you need to know two other important concepts which Prolog programmers use extensively.
These are:
Input and Output parameters: There are no return statements in Prolog. Some variables will be results (outputs) and some others will be the inputs. In your program, the first and second parameters are input and the last one is the output.
Pattern Matching: If a list is expressed as [Head|Tail]. Head is the first element and Tail is a list of the remaining elements.
When you call app, for example, as app(5, [1, 2, 3, 4], L)., Prolog interpreter tries to come up with values for L such that app is true.
Prolog interpreter is solving the problem in the following steps:
In order to make app(X, [Y | S], [Y | S2]) true, the first element of the last parameter need to become Y. So, in my example, L becomes [1, S2].
Then it tries to match the rule app(X, S, S2). Here S is [2, 3, 4] and S2 is the output parameter for the next run. Then Step 1 gets repeated but with app(5, [2, 3, 4], S2) and after that S2 becomes [2, S2]. So, L, now, becomes [1, 2, S2].
This same thing gets repeated (recursion) and L is populated as [1, 2, 3, 4, S2].
Now, the second parameter is empty. So, the first fact app(X, [], [X]) is matched. In order to make this true, the last parameter becomes a list containing just X (which is 5 in this case), which results in L being [1, 2, 3, 4, 5].
Related
I am trying to keep only the first element and the last element for a list which contains only consecutive integers.
For example:
?- remove([1,2,3,4,5], NewList).
NewList = [1,5].
I can only successfully keep the last element:
remove([], []).
% for consecutive integers in the list
remove([ Head | Tail ], NewList) :-
check_consecutive(Head, Tail),
remove(Tail, NewList).
% for the case when the list is empty
remove([ Head | Tail ], [ Head | NewList ]) :-
not(check_consecutive(Head, Tail)),
remove(Tail, NewList).
check_consecutive(Num, [ Head | _ ]) :-
Num is Head - 1.
I have been tying to keep the first element, but it keeps giving me the last element.
If there are some elements which is not consecutive, it should do some thing like:
?- remove([1,2,4,5,6,8,3], NewList).
NewList = [[1,6], 8, 3].
Any assistance is appreciated.
To solve this problem you have to handle different cases, here the solution then the comment:
last([E],E).
last([_|T],E):-
last(T,E).
remove([],[]).
remove([A],[A]).
remove([A,B],[A,B]).
remove([A|B],[A,Last]):-
last(B,Last).
findElementsR([A,B],LT,LOT,LO):-
B =< A,
append(LT,[A],LT1),
remove(LT1,LRem),
append(LOT,[LRem,[B]],LO).
findElementsR([A,B|T],LT,LOT,LO):-
B =< A,
append(LT,[A],LT1),
remove(LT1,LRem),
append(LOT,[LRem],LOT1),
findElementsR([B|T],[],LOT1,LO).
findElementsR([A,B],LT,LOT,LO):-
B is A+1, %consecutive
append(LT,[A,B],LTO),
remove(LTO,RM),
append(LOT,[RM],LO).
findElementsR([A,B|T],LT,LOT,LO):-
B is A+1, %consecutive
append(LT,[A],LTO),
findElementsR([B|T],LTO,LOT,LO).
findElements(L,LO):-
findElementsR(L,[],[],LO).
?- findElements([3,1,2,3,2,3,4,5,2,3,4],L).
L = [[3], [1, 3], [2, 5], [2, 4]]
false
So, first of all, i've defined last/2 that simply, given a list as an input, returns the last element
?- last([1,2,3],L).
L = 3
Then with remove/3 i get the list composd by th first and the last element.
findElements/2 is used to call findElementsR/4 (to make it tail recursive). findElements/4 finds a list of consecutive elements and then calls remove/2 to get the first and the last.
I'm trying to append the sum of a list within the given list, but not getting exactly what I need with this:
list_sum([],[]).
list_sum([[Lname|[Lvalues|_]]|List],X):-
sum(Lvalues,Sum),
app([Lname,Lvalues,Sum],[Out],X),
list_sum(List,Out).
Let my input be:
list_sum([[list1,[1,1]],[list2,[2,2]]],X).
My output is:
X = [list1, [1, 1], 2, [list2, [2, 2], 4, []]].
But was expecting:
X = [[list1, [1, 1], 2], [list2, [2, 2], 4]].
I showed this example for two lists, but I'm also trying to make it work for any amount of lists, including one, but my output just gets even worse.
What you have is fairly close, but there are a couple of issues.
Your base case is good (list_sum([], []).). The head of your recursive clause is oddly stated:
list_sum([[Lname|[Lvalues|_]] | List], X) :- ...
The head of your first argument list, [Lname | [Lvalues|_]] is equivalent to [Lname, Lvalues | _]. Since you plan a two element list as each element, _ would always be []. So then it's equivalent to [Lname, Lvalues]. So the head of the recursive clause becomes:
list_sum([[Lname, Lvalues] | List], X) :- ...
The next statement, sum(Lvalues,Sum) looks fine. The following append is incorrect:
app([Lname,Lvalues,Sum],[Out],X)
Your intention is to have [Lname, Lvalues, Sum] to be the head of a new list, with Out as a tail. That looks like, [[Lname, Lvalues, Sum]|Out]. But the above append will give you, [Lname, Lvalues, Sum | Out] which isn't the same thing. And you don't need the app call. Simply, X = [[Lname, Lvalues, Sum]|Out] will do.
Now your recursive call looks like this:
list_sum([[Lname, Lvalues] | List], X) :-
sum(Lvalues, Sum),
X = [[Lname, Lvalues, Sum]|Out],
list_sum(List, Out).
Which should yield the results you're after. You can simplify this a little by including the unification of X directly in the head of the clause:
list_sum( [[Lname, Lvalues] | List], [[Lname, Lvalues, Sum]|Out]) :-
sum(Lvalues, Sum),
list_sum(List, Out).
I'm trying to understand prolog but I am stuck with one example, can you explain to me how is prolog going through this call:
eli(2,[2,2,1],L).
using those facts:
eli(X,[],[]).
eli(X,[Y],[Y]).
eli(X,[X,X|L],L1) :- eli(X,L,L1).
eli(X,[Y|L],[Y|L1]) :- eli(X,L,L1).
The results are:
L = [1]
L = [1]
L = [2, 2, 1]
L = [2, 2, 1]
and I'm not really sure why.
Thanks in advance!
It looks like your predicate is mean to delete two consecutive appearance of any element.
First clause, if the target list is empty, return the empty list. In this case the X variable in the fact is not necessary. Replace X by te anonymous variable.
eli(_,[],[]).
Second clause is similar to the first, but it matches the target list if it contains only one element. Variable X is also not necessary.
eli(_,[Y],[Y]).
Third clause, if the target list contains two or more elements, and in the Head of the list both elements are equal to X, don't copy this two elements to the Result list, and make a recursive call to the eli/3 predicate in the body of the rule, to continue the search.
eli(X,[X,X|L],L1) :- eli(X,L,L1), !.
In this case we add the cut predicate, to avoid backtracking after this rule succeeded. Otherwise you may get undesired results, like L = [2, 2, 1] in your test.
And the last clause, copy the element in the Head of the Target list to the Result list, and continue the recursive call, this will stop when the Target list is empty or contains only one element (your first two clauses).
eli(X,[Y|L],[Y|L1]) :- eli(X,L,L1).
Now this is your predicate eli/3:
eli(_,[],[]).
eli(_,[Y],[Y]).
eli(X,[X,X|L],L1) :- eli(X,L,L1), !.
eli(X,[Y|L],[Y|L1]) :- eli(X,L,L1).
Test:
?- eli(2,[2,2,1],L).
L = [1]
?- eli(2,[1,2,2,3],L).
L = [1, 3]
I know how to do it for 2 lists:
append([],L,L).
append([H|T],L,[H|R]):-append(T,L,R).
but how to do it for 3? Without using the append for 2 lists twice.
To append lists efficiently, consider using difference lists. A difference list is a list expressed using a term with two lists. The most common representation uses (-)/2 as the functor for the term. For example, the list [1,2,3] can be expressed as:
[1,2,3| Tail]-Tail.
By keeping track of the list tail, i.e. of its open end, you can do several operations efficiently. For example, you can append an element to end of the list in O(1) by instantiating the tail:
add_to_end_of_list(List-Tail, Element, List-Tail2) :-
Tail = [Element| Tail2].
Or simply:
add_to_end_of_list(List-[Element| Tail2], Element, List-Tail2).
Let's try it:
?- add_to_end_of_list([1,2,3| Tail]-Tail, 4, Result).
Tail = [4|_G1006],
Result = [1, 2, 3, 4|_G1006]-_G1006.
Now, appending two lists is similar and also O(1). Instead of appending an element, we want to append a list of elements:
dappend(List1-Tail1, Tail1-Tail2, List1-Tail2).
For example:
?- dappend([1,2,3 | Tail1]-Tail1, [4,5,6| Tail2]-Tail2, Result).
Tail1 = [4, 5, 6|Tail2],
Result = [1, 2, 3, 4, 5, 6|Tail2]-Tail2.
I leave to you as an exercise to answer your own question using difference lists. Note that going from a difference list to a closed list, is simply a question of instantiating the open end to the empty list. For example:
?- dappend([1,2,3 | Tail1]-Tail1, [4,5,6| Tail2]-Tail2, Result-[]).
Tail1 = [4, 5, 6],
Tail2 = [],
Result = [1, 2, 3, 4, 5, 6].
However, going from a closed list to a difference list does requires you to traverse the list, which is O(n):
as_difflist([], Back-Back).
as_difflist([Head| Tail], [Head| Tail2]-Back) :-
as_difflist(Tail, Tail2-Back).
The cost of constructing the difference lists may or may not be an issue, of course, depending on how you get the initial lists and how often you will be appending lists in your application.
Hope I understood the question (and I don't think the following is more efficient than the other solutions here), but did you mean something like this?
append([],[],L,L).
append([],[H|T],L,[H|R]) :- append([],T,L,R).
append([H|T],L0,L1,[H|R]) :- append(T,L0,L1,R).
append3(Xs, Ys, Zs, XsYsZs) :-
append(Xs, YsZs, XsYsZs),
append(Ys, Zs, YsZs).
Is as efficient, as it can get. Cost is about |Xs|+|Ys| inferences. However, you might have attempted to define it like the following with about 2|Xs|+|Ys| inferences.
append3bad(Xs, Ys, Zs, XsYsZs) :-
append(Xs, Ys, XsYs),
append(XsYs, Zs, XsYsZs).
Also, termination is much better in the first case:
append3(Xs, Ys, Zs, XsYsZs)
terminates_if b(Xs),b(Ys);b(XsYsZs)
meaning that either Xs and Ys or XsYsZs needs to be known to make append3/4 terminate
... versus
append3bad(Xs, Ys, Zs, XsYsZs)
terminates_if b(Xs),b(Ys);b(Xs),b(XsYsZs)
^^^^^
for append3bad/4, where XsYsZs is not sufficient, but additionally also Xs has to be known.
I have this
initialstate(0,[],[[1,0],[2,3],[1,2],[2,3]]).
I would like to find which sublist inside the list is the same with the number 1.
And after delete the sublist who had the number one.
It would be something like that :
?- initialstate(_,_,[X,_]), initialstate(_,_,list),
delete(list,X,Newlist),assertz(initialstate(A,B,Newlist)).
I know this is wrong but i am trying to explain you what i want to do.
I want my final list to be :
initialstate(0,[],[[2,3],[2,3]]).
Edit: A new answer to incorporate CapelliC's endorsement of delete/3 and OPs further queries in the comments.
Predicate:
initial_state_without_elements(initialstate(A,B,List), Element,
initialstate(A,B,FilteredList) ):-
delete(List, Element, FilteredList).
Query:
?- initial_state_without_elements(initialstate(0,[],[[1,0],[2,3],[1,2],[2,3]]), [2,_], NewState).
NewState = initialstate(0, [], [[1, 0], [1, 2]]).
We want to take some list of lists, ListOfLists, and remove all the sublists, Ls, that contain a given Element. In general, to check if an element X is in some list List, we we can use member(X, List).
So we want a list of lists, SubListsWithout, which contains all Ls of ListOfLists for which member(Element, SubList) is false.
Here is a predicate sublists_without_elements/3, which takes a list of lists, an element, and a variable as arguments, and unifies the variable with a list of the sublists of the first which do not contain the element. This predicate uses the standard recursive technique for describing lists:
sublists_without_element([], _, []).
sublists_without_element([L|Ls], Element, SubListsWithout) :-
member(Element, L), !,
sublists_without_element(Ls, Element, SubListsWithout).
sublists_without_element([L|Ls], Element, [L|SubListsWithout]) :-
sublists_without_element(Ls, Element, SubListsWithout).
The first clause is our base case: the empty list has no sublists, regardless of the element.
The second clause is true if (1) Element is a member of L, and (2) SubListsWithout is a list of the sublists of Ls which do not contain Element. (note: *L has not been added to SubListsWithout in this clause, which means it has been excluded from the lits we are accumulating. The ! is used to prune the search path here, because once we know that an Element is a member of L, we don't want to have anything to do with L again.)
The third clause is true if L is added to SubListsWithout, and SubListsWithout contains the rest of the sublists of Ls which do not have Element as a member.
The above is one way to write your own predicate to filter a list. It is important that you be able to write and read predicates of this form, because you will see tons of them. However, you'll also want to get to know the standard libraries of your Prolog implementation. In SWI-Prolg, you can simply use exclude\3 to accomplish the above task. Thus, you could achieve your desired goal in with the following:
filter_initial_state(initial_state(A,B,List),
Element,
initial_state(A,B,FilteredList)) :-
exclude(member(Element), List, FilteredList).
You can use it thus,
?- filter_initial_state(initial_state(0,[],[[1,0],[2,3],[1,2],[2,3]]), 1, Filtered).
and prolog will reply,
Filtered = initial_state(0, [], [[2, 3], [2, 3]]).
I think you've chosen the right builtin (delete/3), but there is some detail wrong. Here is a working 'query':
?- retract(initialstate(A,B,C)), C=[[X,_]|_], delete(C,[X,_],Newlist), assertz(initialstate(A,B,Newlist)).
A = 0,
B = [],
C = [[1, 0], [2, 3], [1, 2], [2, 3]],
X = 1,
Newlist = [[2, 3], [2, 3]].
First of all: if you do a assertz without first doing a retract you'll end with having an almost duplicate of your data, and probably is not what you want. assertz stores the updated initialstate after the old one (there is asserta, but I doubt will correct the bug).
Second, note how to use pattern matching to extract the essential information:
C=[[X,_]|_]
the use of _ (i.e. an anonymous var) is essential, because allows to specify which part of the complex structure we must ignore when using it. And we must use it also to indicate to delete/3 what to match.
delete(C,[X,_],Newlist)