I am having trouble understanding the codes below.
Can someone explain step by step what is happening if I have the follow input:
append([1,2,3], Lst).
Actually, I don;t get how 1 and 2 is appended the list Lst as a result.
append([_], []).
append([H|T], [H|N]) :- append(T,N).
It sounds like you're new to Prolog. If so, welcome! Let's analyze this.
This unfortunately-named function has two clauses. Prolog looks at the clauses in order to see which one applies. When it finds one that matches, it tries to perform it. If there's a failure somewhere in performing it, it will back up and try the next option. Where precisely these choice points are varies depending on the program; in this program, the only one will be at the clause level, deciding which rule to use.
One way of looking at the first rule is that it is saying "A list with one element, regardless of what that element is, is related to the empty list." Looking at append([_], [])
, if we had X = [foo]
and Y = []
, it would hold, because [foo]
is a one-item list and []
is the empty list. This rule is good Prolog style, because it will work regardless of instantiation: we could supply the left or the right or neither, it doesn't matter.
The second clause is also quite simple. It says that the left argument and the right argument are related if they both start with the same item, and if the rest of the lists are also related by this same predicate. In other words, if I have two lists X
and Y
such that append(X, Y)
is true, then append([H|X], [H|Y])
is also true. It doesn't matter what H is and it doesn't matter what X and Y are, except insofar as would be implied by append/2
.
Thinking logically, if I know that any one-item list is related to the empty list, and any list is related to a list that starts with the same item and otherwise is the same, the only kinds of lists that can be so related are lists where every item is the same, except the left list has one more item in it at the end which is not present on the right. So [1,2,3,4] is related to [1,2,3], but so is [1,2,3,foo] and [1,2,3].
Procedurally, let's look at what happens as this predicate is processed with this set of arguments:
append([1,2,3], X).
The first rule won't match on [1,2,3]. So we must look at the second rule:
append([1|[2,3]], [1|X]) :- append([2,3], X).
We can repeat:
append([2|[3]], [2|Y]) :- append([3], Y).
Now the first rule does match:
append([3], []).
So putting it all together:
append([1,2,3], [1|X]) implies
append([2,3], X=[2|Y]) implies
append([3], Y=[])
so Y = []
so X = [2]
so the right side is [1,2].
A Prolog trace will show you basically the same information:
?- trace, append([1,2,3], X).
Call: (7) append([1, 2, 3], _G1633) ? creep
Call: (8) append([2, 3], _G1752) ? creep
Call: (9) append([3], _G1755) ? creep
Exit: (9) append([3], []) ? creep
Exit: (8) append([2, 3], [2]) ? creep
Exit: (7) append([1, 2, 3], [1, 2]) ? creep
What makes this Prolog code confusing is that it doesn't look like you've told Prolog how to do anything. And it's true, you haven't, but by specifying what is true logically, Prolog is able to figure it out by itself. This is pretty clever code. If this were Haskell, we'd be talking about the built-in function init
, which returns all of the list but the last item.
Hope this helps!