PrologEZ
Control & the database ยท Lesson 31 of 43

Higher-order predicates

call/N, maplist, foldl, filters, and lambdas.

In Prolog, code is data. call/N takes a goal (or part of one) and runs it, adding extra arguments:

?- call(write, hello)
?- G = format("~w and ~w~n"), call(G, [a, b])
?- call(plus(1), 2, X)

plus(1) is a partial goal; call(plus(1), 2, X) appends the arguments and runs plus(1, 2, X). This is currying, and it makes the list-processing predicates below possible.

maplist: apply a relation to every element

double(X, Y) :- Y is X * 2.
even(X) :- 0 is X mod 2.
add(X, Acc0, Acc) :- Acc is Acc0 + X.
?- maplist(double, [1, 2, 3], L)
?- maplist(double, L, [2, 4, 6])
?- maplist(succ, L, [2, 3, 4])
?- maplist(atom, [a, b, c])
?- maplist([X, Y, Z]>>(Z is X + Y), [1, 2, 3], [10, 20, 30], Sums)

maplist/3 also works backwards when the relation does (succ/2 does; our double/2, built on is, can't, as the error above shows).

Filtering and folding

?- include(even, [1, 2, 3, 4, 5, 6], L)
?- exclude(even, [1, 2, 3, 4, 5, 6], L)
?- partition(even, [1, 2, 3, 4, 5], In, Out)
?- foldl(add, [1, 2, 3, 4], 0, Sum)
?- foldl([X, A0, A]>>(A is max(X, A0)), [3, 9, 2], 0, Max)

foldl(Goal, List, Start, Result) threads an accumulator through the list: Goal is called as call(Goal, Element, AccBefore, AccAfter).

Lambdas with >>

Writing a helper predicate for every tiny operation is tedious. The yall library lets you write anonymous predicates inline:

[X1, X2, ...] >> Body

Parameters are listed before >>. Variables from the surrounding clause are visible in the body, if they're already bound when the lambda runs:

scale(K, List, Scaled) :- maplist([X, Y]>>(Y is X * K), List, Scaled).
only_big(Min, List, Big) :- include([X]>>(X >= Min), List, Big).
?- scale(10, [1, 2, 3], L)
?- only_big(5, [3, 8, 5, 1, 9], L)

Building goals at run time

=.. and call/1 let a program assemble and execute goals dynamically:

?- Goal =.. [format, "~w squared is ~w~n", [4, 16]], call(Goal)
?- forall(member(Op, [+, -, *]), (G =.. [Op, 6, 3], X is G, format("~w = ~w~n", [G, X])))

Exercise: Sum of squares

Write sum_of_squares(List, Sum) using maplist (with a lambda or helper) and sum_list. For example sum_of_squares([1, 2, 3], S) gives S = 14.