Reputation:
I am going through some past exam questions for my prolog exam that is coming up.
Here is the question:
(a) Write a predicate insert(Xs, Y, Zs)
that holds when Zs
is the list obtained
by inserting Y
into the list Xs
. A query such as:
? - insert([1,2,3], 4, Zs).
should succeed four times and give the following answers:
Zs = [4, 1, 2, 3]
Zs = [1, 4, 2, 3]
Zs = [1, 2, 4, 3]
Zs = [1, 2, 3, 4].
I'm a bit concerned because I have no idea where to start. Would anyone be able to help out as I need example solutions to practice for my exam.
Would really appreciate any help with this.
Upvotes: 0
Views: 614
Reputation: 40768
We start by changing the terrible name of this predicate: The predicate should describe what holds, not what to do. The name should reflect that. I suggest list_with_element/3
, and encourage you to try finding even better names, ideally making clear what each argument stands for.
Then, we do what we set out to do: Describe the cases that make this relation hold.
For example:
list_with_element([], E, [E]).
list_with_element([L|Ls], E, [E,L|Ls]).
list_with_element([L|Ls0], E, [L|Ls]) :-
...
I leave filling in the ...
as an easy exercise. State the condition that is necessary for the clause head to be true!
EDIT: I would like to say a bit more about the pattern above. In my experience, a good way—and definitely in the beginning—to reason about predicates that describe lists is to consider two basic cases:
[]
, denoting the empty list'.'(E, Es)
, also written as [E|Es]
, where E
is the first element of the list and Es
is again a list.This follows the inductive definition of lists.
The drawback in this specific case is that this approach leads to a situation where case (2) again needs to be divided into two subcases, and somehow unexpectedly necessitates three clauses to handle the two basic cases. This obviously runs counter to our intuitive expectation that two clauses should suffice. Indeed they do, but we need to be careful not to accidentally lose solutions. In this case, the first two clauses above are both subsumed by the fact:
list_with_element(Ls, E, [E|Ls]).
Every experienced Prolog coder will write such predicates in this way, or just, as in this case, use select/3
directly. This is what @lurker sensed and hinted at, and @tas correctly shows that a different clause (which is easy to come up with accidentally) does not fully subsume all cases we want to express.
Thus, I still find it a lot easier to think first about the empty list explicitly, make sure to get that case correct, then continue with more complex cases, and then see if you can write the existing program more compactly. This is the way I also used for this sample code, but I did not make it as short as possible. Note that with monotonic code, it is completely OK to have redundant facts!
Note that is is specifically not OK to just replace the first two clauses by:
list_with_element([L|Ls], E, [E|Ls]).
because this clause does not subsume case (1) above.
Upvotes: 3
Reputation: 4078
There is a built-in predicate select/3
that does the same thing, although with the arguments in a different order.
Remember that (if defined correctly) a predicate can work in different directions. For instance, it can tell you what a list would look like after removing an element, it can (although it's fairly trivial) tell you what element to remove from one list to get another, or it can tell you what lists, after having a given element removed, would resemble a given list.
(Hint: you may want to look into that last one).
Upvotes: 1
Reputation:
I guess that one answer that the question might be looking for goes along these lines:
insert(List, Element, NewList) :-
append(Front, Back, List), % split list in two
append(Front, [Element|Back], NewList). % reassemble list
If you would like a declarative reading:
NewList has Element between the front and the back of List.
Check carefully if append/3
or a predicate with the same semantics appears in the earlier questions or the study material.
And note that this is in essence the exact same solution as the suggestion by @mat, if I understand it correctly. Consult the textbook definition of append/3
for details. Or even better, look at the textbook definition of append/3
and adapt it to use if for "inserting".
Upvotes: 1