Processing lists in Prolog - 2

32
06-25433 – Logic Programming Processing lists in Prolog - 2 This lecture shows that techniques introduced before (analysing terminating conditions and recursive programming) can be used to develop more complex procedures.

Transcript of Processing lists in Prolog - 2

Page 1: Processing lists in Prolog - 2

06-25433 – Logic Programming

Processing lists in Prolog - 2

This lecture shows that techniques introduced before (analysing terminating conditions and recursive programming) can be used to develop more complex procedures.

Page 2: Processing lists in Prolog - 2

06-25433 – Logic Programming

8 - Processing lists in Prolog: 2 1

This lecture is about:

– writing procedures with one or more terminating or recursive clauses;

– deleting one or all instances of an element from a list;

– The effects of matching v. unification;

– changing the order in which solutions are presented by changing clause order.

Page 3: Processing lists in Prolog - 2

06-25433 – Logic Programming

8 - Processing lists in Prolog: 2 2

Last time:

Terminating at the end of the list

For instance counting all elements:

% 1 - terminating

count_elem([], Total, Total).

% 2 - recursive

count_elem([Hd|Tail], Sum, Total) :-

Sum1 is Sum + 1,

count_elem([Hd|Tail], Sum1, Total).

Terminates at the

end of the list.

Page 4: Processing lists in Prolog - 2

06-25433 – Logic Programming

8 - Processing lists in Prolog: 2 3

Last time:

Terminating when given element is found

For instance finding a given element:

% 1 - terminating

elem(Elem, [Elem|_]).

% 2 - recursive

elem(Elem, [_|Tail]) :-

elem(Elem, Tail).

Notice, this can be run “backwards” to enumerate the

individual elements of a list.

Demo1

Terminates before

the end of the list.

Page 5: Processing lists in Prolog - 2

06-25433 – Logic Programming

8 - Processing lists in Prolog: 2 4

Last time: Terminating when given

number of elements have been scanned

% 1 – recursive

nth(Count, Item, [_|Tail]) :-

Count > 1,

Count0 is Count - 1,

nth(Count0, Item, Tail).

% 2 – terminating

nth(1, Head, [Head|_]).

Demo2

The code

counts down

from the given

position to 1.

Page 6: Processing lists in Prolog - 2

06-25433 – Logic Programming

8 - Processing lists in Prolog: 2 5

Consolidation moment

Three main ways to halt recursion in list processing:

1. at the end of the list ([]);

2. when a specific element is found;

3. when a specific position in a list is reached.

Page 7: Processing lists in Prolog - 2

06-25433 – Logic Programming

8 - Processing lists in Prolog: 2 6

More than one recursive clause

We’ve seen an example with two recursive clauses:

classify([], [], []).

classify([Head|Tail], [Head|Numbers],

Atoms) :-

number(Head),

classify(Tail, Numbers, Atoms).

classify([Head|Tail], Numbers,

[Head|Atoms]) :-

atom(Head),

classify(Tail, Numbers, Atoms).

Page 8: Processing lists in Prolog - 2

06-25433 – Logic Programming

8 - Processing lists in Prolog: 2 7

More than one terminating clause - 1

It is sometimes necessary to have more than one terminating clause.

Consider the task of pairing the elements of two lists with any elements left over added to the end of the list:

| ?- pair([ann,bel], [joe,bob,sam], Res).

Res = [<,ann,joe,>,<,bel,bob,>,sam] ? ;

no

Page 9: Processing lists in Prolog - 2

06-25433 – Logic Programming

8 - Processing lists in Prolog: 2 8

More than one terminating clause - 2

Terminating conditions?

– when both lists are empty: •pair([], [], []).

– when first list is empty; second isn’t: •pair([], List, List).

– when first list isn’t empty; second is empty: •pair(List, [], List).

Page 10: Processing lists in Prolog - 2

06-25433 – Logic Programming

8 - Processing lists in Prolog: 2 9

More than one terminating clause - 3

The recursive clause is:

pair([Head1|Tail1], [Head2|Tail2],

['<', Head1, Head2, '>'|Tail3]) :-

pair(Tail1, Tail2, Tail3).

Page 11: Processing lists in Prolog - 2

06-25433 – Logic Programming

8 - Processing lists in Prolog: 2 10

More than one terminating clause - 4

Unfortunately, this procedure doesn’t work properly:

Demo3

Demo4

When 1st & 2nd arguments are reduced to [], the last goal is: pair([], [], Res).

This unifies with the terminating clauses: pair([], [], []).

pair([], List, List).

pair(List, [], List).

Page 12: Processing lists in Prolog - 2

06-25433 – Logic Programming

8 - Processing lists in Prolog: 2 11

More than one terminating clause - 5

The solution is to write mutually exclusive terminating clauses:

– When the first list is empty and the second isn’t: pair([], [Head|Tail], [Head|Tail]).

– When the first list isn’t or isn’t empty and the second is empty: pair(List, [], List).

Page 13: Processing lists in Prolog - 2

06-25433 – Logic Programming

8 - Processing lists in Prolog: 2 12

Deleting elements from a list - 1

Consider delete_1/3:

% 1 - terminating

delete_1(Head, [Head|Tail], Tail).

% 2 - recursive

delete_1(Item, [Head|Tail],

[Head|New_Tail]) :-

delete_1(Item, Tail, New_Tail).

and the query: delete_1(Var, [a,b,a,b], Pruned_List).

Page 14: Processing lists in Prolog - 2

06-25433 – Logic Programming

8 - Processing lists in Prolog: 2 13

Deleting elements from a list - 2

It is obvious that Var will be bound to a, b, a & b in turn, but what about the goal?

delete_1(b, [a,b,a,b], Pruned_List).

• There are two ways of satisfying this goal.

• If there is local failure in a program containing

delete_1/3, the next entry is taken off the stack

and tried – until none is left. Demo5

Page 15: Processing lists in Prolog - 2

06-25433 – Logic Programming

8 - Processing lists in Prolog: 2 14

Variations on deletion

There are two obvious variants of delete_1/3:

• Delete one and only one occurrence of an element;

• Delete all occurrences of an element.

These show two extensions of list processing.

Page 16: Processing lists in Prolog - 2

06-25433 – Logic Programming

8 - Processing lists in Prolog: 2 15

Delete one and only one element

A very easy extension of delete_1/3:

The clauses of delete_1/3 aren’t mutually exclusive.

An extra line ensure they are mutually exclusive:

delete_2(Head, [Head|Tail], Tail).

delete_2(Item, [Head|Tail],

[Head|New_Tail]) :-

Item \= Head,

delete_2(Item, Tail, New_Tail).

Page 17: Processing lists in Prolog - 2

06-25433 – Logic Programming

8 - Processing lists in Prolog: 2 16

Delete all instances of an element - 1

This requires more thought:

• different from delete_1/3 because all elements of the “input” list must be scanned;

• a “terminate-at-end of list” pattern;

• the head of the “input” list either matches the element to be deleted OR it does not match the element to be deleted;

• OR-choice has to be represented by two recursive rules.

Page 18: Processing lists in Prolog - 2

06-25433 – Logic Programming

8 - Processing lists in Prolog: 2 17

Delete all instances of an element - 2

The terminating clause:

When the “input” list is empty, then the output list is “empty”.

delete_all(_Head, [], []).

Page 19: Processing lists in Prolog - 2

06-25433 – Logic Programming

8 - Processing lists in Prolog: 2 18

Delete all instances of an element - 3

The recursive clauses:

When the heads of the “input” and “output” lists don’t match the element being deleted:

% 2 - recursive: head doesn't match

delete_all(Item, [Head|Tail],

[Head|New_Tail]) :-

Item \= Head,

delete_all(Item, Tail, New_Tail).

Page 20: Processing lists in Prolog - 2

06-25433 – Logic Programming

8 - Processing lists in Prolog: 2 19

Delete all instances of an element - 4

The recursive clauses:

When the heads of the “input” list matches the element being deleted:

% 3 - recursive: head does match

delete_all(Item, [Item|Tail],

New_Tail) :-

delete_all(Item, Tail, New_Tail).

Demo6

Page 21: Processing lists in Prolog - 2

06-25433 – Logic Programming

8 - Processing lists in Prolog: 2 20

Matching v. unification - 1

Look again at the deletion of one element:

% 1 - terminating

delete_1(Head, [Head|Tail], Tail).

% 2 - recursive

delete_1(Item, [Head|Tail],

[Head|New_Tail]) :-

delete_1(Item, Tail, New_Tail).

Page 22: Processing lists in Prolog - 2

06-25433 – Logic Programming

8 - Processing lists in Prolog: 2 21

Matching v. unification - 2

What happens with the query?

| ?- delete_1(Var, [a,b,a,b], New_List).

Var = a,

New_List = [b,a,b] ? ;

Var = b,

New_List = [a,a,b] ? ;

no

Page 23: Processing lists in Prolog - 2

06-25433 – Logic Programming

8 - Processing lists in Prolog: 2 22

Matching v. unification - 3

Suppose we want to have matching instead of

unification:

| ?- 'delete=='(Var, [a,b,a,b], New_List).

no

| ?- 'delete=='(b, [a,b,a,b], New_List).

New_List = [a,a,b] ? ;

no

Page 24: Processing lists in Prolog - 2

06-25433 – Logic Programming

8 - Processing lists in Prolog: 2 23

Matching v. unification - 4

The program needs two additions:

% 1 - terminating

'delete=='(Item, [Head|Tail], Tail) :-

Item == Head.

% 2 - recursive

'delete=='(Item, [Head|Tail],

[Head|New_Tail]) :-

Item \== Head,

'delete=='(Item, Tail, New_Tail).

Common mistake:

use \=/2.

Page 25: Processing lists in Prolog - 2

06-25433 – Logic Programming

8 - Processing lists in Prolog: 2 24

The effect of clause order - 1

So far, we’ve generally placed the terminating clause first in a procedure. What effect does the order of clauses have?

Consider again delete_1/3:

% 1 - terminating

delete_1(Head, [Head|Tail], Tail).

% 2 - recursive

delete_1(Item, [Head|Tail],

[Head|New_Tail]) :-

delete_1(Item, Tail, New_Tail).

Page 26: Processing lists in Prolog - 2

06-25433 – Logic Programming

8 - Processing lists in Prolog: 2 25

The effect of clause order - 2

If we use a non-deterministic query:

| ?- delete_1(a, [a,1,a,2,a,3], Res).

Res = [1,a,2,a,3] ? ;

Res = [a,1,2,a,3] ? ;

Res = [a,1,a,2,3] ? ;

no

Page 27: Processing lists in Prolog - 2

06-25433 – Logic Programming

8 - Processing lists in Prolog: 2 26

The effect of clause order - 3

If we swap the clauses around in our procedure:

% 2 - recursive

delete_1(Item, [Head|Tail],

[Head|New_Tail]) :-

delete_1(Item, Tail, New_Tail).

% 1 - terminating

delete_1(Head, [Head|Tail], Tail).

Page 28: Processing lists in Prolog - 2

06-25433 – Logic Programming

8 - Processing lists in Prolog: 2 27

The effect of clause order - 4

and use the same query:

| ?- delete_1(a, [a,1,a,2,a,3], Res).

Res = [a,1,a,2,3] ? ;

Res = [a,1,2,a,3] ? ;

Res = [1,a,2,a,3] ? ;

no

Page 29: Processing lists in Prolog - 2

06-25433 – Logic Programming

8 - Processing lists in Prolog: 2 28

The effect of clause order - 5

Compare the bag of solutions:

Recursive first:

[a,1,a,2,3]

[a,1,2,a,3]

[1,a,2,a,3]

Terminating first:

[1,a,2,a,3]

[a,1,2,a,3]

[a,1,a,2,3]

Page 30: Processing lists in Prolog - 2

06-25433 – Logic Programming

8 - Processing lists in Prolog: 2 29

The effect of clause order - 6

In summary:

Clause order shouldn’t matter in a declarative language because process is not significant.

But that isn’t always true:

Out-of-lecture activity:

Experiment with clause ordering and the query:

| ?- delete_all(Var, List, [a,b,a,b]).

Page 31: Processing lists in Prolog - 2

06-25433 – Logic Programming

8 - Processing lists in Prolog: 2 30

This time …

More advanced use of Prolog lists allows us to:

– use more than one terminating clause (eg pair/3);

– apply an operation to all matching list elements (delete_all/3);

– use matching (==/2) instead of unification;

– change the “order of solutions” by changing clause order.

Page 32: Processing lists in Prolog - 2

06-25433 – Logic Programming

31

Next time …

A last look at lists, specifically:

– lists as stacks and queues;

– processing lists within lists;

– making list processing more efficient;

– stopping Prolog running out of stack space and/using too much memory.

31 8 - Processing lists in Prolog: 2