Prolog Programming (Volume 3) Dr W.F. Clocksin
Mapping: The Full Map sqlist(, ) sqlist([], []). sqlist([X|T], [Y|L]) :- Y is X * X, sqlist(T, L). List of numbers List of squares of numbers
Mapping: The Full Map (cont’d) Here is an exercise in compound terms. Map each list element (a number) to a term s(A,B) where A is the number and B is its square. Input: [1, 9, 15] Output: [s(1,1), s(9, 81), s(15, 225)] sqterm([], []). sqterm([X|T], [s(X,Y)|L]) :- Y is X * X, sqterm(T, L).
General Scheme for Full Map /* fullmap(In, Out) */ fullmap([], []). fullmap([X|T], [Y|L]) :- transform(X,Y), fullmap(T, L). Here is a typical transformation table…
Simple Transformation transform(cat, gatto). transform(dog, cane). transform(hamster, criceto). transform(X, X). ?- fullmap([cat, dog, goat], Z). Z = [gatto, cane, goat] A ‘catchall’ rule
Worksheet 11: Multiple Choices Sometimes the map needs to be sensitive to the input data: Input: [1, 3, w, 5, goat] Output: [1, 9, w, 25, goat] squint([], []). squint([X|T], [Y|L]) :- integer(X), Y is X * X, squint(T, L). squint([X|T], [X|L]) :- squint(T,L). Just use a separate clause for each choice
Multiple Choices Using the infix binary compound term *, it is easy enough to give some mathematical reality to the map: Input: [1, 3, w, 5, goat] Output: [1, 9, w*w, 25, goat*goat] squint([], []). squint([X|T], [Y|L]) :- integer(X), Y is X * X, squint(T, L). squint([X|T], [X*X|L]) :- squint(T,L).
Worksheet 12: Partial Maps Given an input list, partially map it to an output list. evens([], []). evens([X|T], [X|L]) :- 0 is X mod 2, evens(T, L) evens([X|T], L) :- 1 is X mod 2, evens(T, L). ?- evens([1, 2, 3, 4, 5, 6], Q), Q = [2, 4, 6].
General Scheme for Partial Maps partial([], []). partial([X|T], [X|L]) :- include(X), partial(T, L) partial([X|T], L) :- partial(T, L). For example, include(X) :- X >= 0. ?- partial([-1, 0, 1, -2, 2], X), X = [0, 1, 2].
Partial Maps Exercise: Write a program that ‘censors’ and input list, by making a new list in which certain prohibited words do not appear. To do this, define a predicate prohibit such that prohibit(X) succeeds if X is a censored word. For example, prohibit(bother). prohibit(blast). prohibit(drat). prohibit(fiddlesticks).
Worked example censor([], []). censor([H|T], T1) :- prohibit(H), censor(T, T1). censor([H|T], [H|T1]) :- censor(T, T1).
Worksheet 13: Removing Duplicates setify([], []). setify([X|T], L) :- member(X,T), setify(T, L). setify([X|T], [X|L]) :- setify(T, L).
WS14: Partial Maps with a Parameter Before, we saw how to prevent loops (when searching a graph) by keeping a ‘trail’ of the nodes visited so far. Here is another way to think about the problem. Keep a list of all the visitable nodes. As each node is visited, strike it off the list and pass the reduced list to the recursive call. Backtracking restores the old list, so alternative paths can be searched.
WS14: Partial Maps with a Parameter First, a predicate to reduce the list. Goal reduce(L,X,M) succeeds for input list L, term X and output list M, when M contains the elements of L except for the first occurrence of X. Thus, X is a parameter that controls which element will be omitted from the output list. reduce([X|T], X, T). reduce([H|T], X, [H|L]) :- reduce(T, X, L).
Use this for searching as follows path(X, X, L). path(X, Y, L) :- a(X, Z), reduce(L, Z, L1), path(Z, Y, L1). Using the arc relation on Worksheet 9, ?- path(a, b, [a, b, c, d, e, f, g, h]). yes.
WS 15: Multiple Disjoint Partial Maps Maps a list into several disjoint lists. Separating sheep from goats. Define the predicate herd, such that the goal herd(L, S, G) succeeds if S is a list of all the sheep in L and G is a list of all the goats in L. herd([], [], []). herd([sheep|T], [sheep|S], G) :- herd(T, S, G). herd([goat|T], S, [goat|G]) :- herd(T, S, G).
What do the following goals do? ?- herd([sheep, goat, goat, sheep, goat], X, Y). ?- herd([goat, sheep, stone, goat, tree], X, Y). ?- herd(X, [sheep, sheep], [goat, goat]).
How to deal with other objects? ?- herd([goat, sheep, stone, goat, tree], X, Y). The above goal fails. Instead, we could ignore other objects by having a catchall: herd([X|T], S, G) :- herd(T, S, G).
Or, collect them in a list also input sheep goats extras herd([], [], [], []). herd([sheep|T], [sheep|S], G, E) :- herd(T, S, G, E). herd([goat|T], S, [goat|G], E) :- herd(T, S, G, E). herd([X|T], S, G, [X|E]) :- herd(T, S, G, E).
Example: Alternating a list Maps an input list into a pair of lists, alternating the elements. alternate([a, b, c, d, e, f], [a, c, e], [b, d, f]) Alternating by index (which element of the input list it is): altx([], [], []). altx([A, B | T], [A | T1], [B| T2]) :- altx(T, T1, T2).
Or, alternating by value Maps an input list into a pair of lists, alternating the elements. altv([1, 2, 3, 4], [2, 4], [1, 3]) altv([], [], []). altv([A | T], [A | T1], B) :- 0 is A mod 2, altv(T, T1, B). altv([B | T], A, [B | T2]) :- 1 is B mod 2, altv(T, A, T2). See how this is the same as sheep and goats?
WS 17: Full maps with state /* mapsum(list of integers, cumulative sum) */ ?- mapsum([1, 3, 2, 5, 4], X). X = [1, 4, 6, 11, 15]. 0
mapsum Use an accumulator as a state variable that helps to determine the value of each element of the output list. /* ms(input list, accumulator, output list) */ mapsum(A, B) :- ms(A, 0, B) ms([], _, []). ms([H|T], N, [C|L]) :- C is H + N, ms(T, C, L). initialise accumulator anonymous variable (don’t care about accumulator if end of list)
WS 18: Sequential Partial Map with State Example: Run length encoding is a useful data representation and compression technique. Represent sequential ‘runs’ of N identical terms T as the term N*T, for example [12, 2, 2, w, 3, 3, s, s, s] maps to [1*12, 2 * 2, 1 * w, 2 * 3, 3 * s].
WS 18: Sequential Partial Map with State [12, 2, 2, w, 3, 3, s, s, s] [1*12, 2 * 2, 1 * w, 2 * 3, 3 * s] runcode(input, current term, current count, output) runcode([], C, N, [N*C]). runcode([H|T], H, N, Z) :- N1 is N+1, runcode(T, H, N1, Z). runcode([H|T], C, N, [N*C|Z]) :- H \== C, runcode(T, H, 1, Z).
Sequential Partial Map with State runcode([], C, N, [N*C]). runcode([H|T], H, N, Z) :- N1 is N+1, runcode(T, H, N1, Z). runcode([H|T], C, N, [N*C|Z]) :- H \== C, runcode(T, H, 1, Z). End of list? Deal with leftover accumulator values Have seen this before... Otherwise, discharge the current run......and start a new run