简体   繁体   中英

Prolog Predicate Solution

I am going through some past exam questions for my prolog exam that is coming up.

Here is the question:

  1. (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.

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:

  1. the atom [] , denoting the empty list
  2. terms of the form '.'(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.

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".

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).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM