07.363 Logic Programming: Lecture 8
Pragmatics
Arithmetic
Type tests
Comparisons
Input/output
Term manipulation
Arithmetic
Why can't Prolog add?
?- 2 = 1 + 1.
no
To Prolog, 1+1 is a term with functor + and two
arguments (both 1). A term with two arguments cannot unify with a
simple integer.
How do you do arithmetic then?
Two ways: peano numbers, and is/2.
Peano numbers
Use Prolog terms to represent unary numbers:
0, s(0), s(s(0)), s(s(s(0))), etc.
Can compare numbers for equality, inequality, etc. using only
unification
peano_add( 0, N, N ).
peano_add( s(N), M, s(Sum) ) :-
peano_add( N, M, Sum ).
| ?- peano_add(s(0), s(s(0)), X ).
X = s(s(s(0)))
| ?- peano_add(s(0), X, s(s(s(0)))).
X = s(s(0))
Real, efficient numbers
Prolog can evaluate a term consisting of integers, reals and terms of
built from (+)/2, (-)/2, (*)/2, (/)/2,
etc.
Non-logical operation! Don't expect reversability!
| ?- X is 1 + 1.
X = 2
| ?- 2 is X + 1.
! Instantiation error in argument 2 of is/2
! goal: 2 is _8997+1
Type tests
Prolog is an untyped language. Runtime type tests are sometimes
needed.
integer/1
float/1
var/1
nonvar/1
atom/1
atomic/1
The tests are non-logical, since they depend on the current state of
variables. E.g.
| ?- var(X), X = 1.
X = 1.
| ?- X = 1, var(X).
no
Example: symbolic differentiation
deriv( N, _X, 0 ) :- number(C).
deriv( X, X, 1 ).
deriv( U^C, X, C*U^L*DU ) :-
number(C),
L is C - 1,
deriv( U, X, DU ).
deriv( -U, X, -DU ) :-
deriv( U, X, DU ).
deriv( U+V, X, DU+DV ) :-
deriv( U, X, DU ),
deriv( V, X, DV ).
deriv( U*V, X, U*DV+V*DU ) :-
deriv( U, X, DU ),
deriv( V, X, DV ).
Comparisons
Compare integers with (>)/2, (<)/2, (>=)/2 and
(=<)/2.
Can also compare terms with (==)/2, (@>)/2, etc. Useful
for generic sorting, binary search, etc.
| ?- freda @> fred.
yes
?- foo(bar) @< bar(foo).
no
Also, compare/3 returns result of comparison (useful for taking
3-way branch)
| ?- compare(Cmp, foo(bar), bar(foo)).
Cmp = >
Input/output
All side-effected, non-logical operations (do you really expect
to backtrack over reading user input???)
Basic i/o operations include
read/1 Read a term from standard input
write/1 Write a term to standard output
get0/1 Read a single character
put/1 Write a single character
format/2 Write formatted output (like printf() in C)
Quintus provides a rich set of i/o operations.
Constructing/destructing terms
How can you write a predicate to substitute all occurences of one term
with another?
E.g.
| ?- subst( x+y*(sqr(x)), x, z, Term ).
Term = z+y*(sqr(z))
subst( X, X, Y, New ) :- !, Y = New.
subst( Const, _, _, New ) :-
atomic(Const),
!,
Const = New.
subst( Term, X, Y, NewTerm ) :-
???
How to look inside a term when you don't know its name or number of
arguments?
Functor/3
functor(Term,Name,Arity) succeeds if Term has principle
functor Name and Arity arguments. Can be used with
Term known to return Name and Arity, or with
Name and Arity known to construct an uninstantiated
term.
E.g.
| ?- functor(foo(a,b), N, A ).
N = foo
A = 2
| ?- functor( Term, foo, 2 ).
Term = foo(_,_)
Arg/3
arg(ArgNum, Term, Arg) unifies Arg with the
ArgNumth argument of Term.
E.g.
?- arg(2, foo(a,b), X).
X = b
Completing subst/4
subst( Term, X, Y, NewTerm ) :-
functor(Term, N, A),
functor(NewTerm, N, A),
subst(N, Term, X, Y, NewTerm ).
subst( N, Term, X, Y, NewTerm ) :-
( N =:= 0 -> true
; arg(N, Term, Arg),
arg(N, NewTerm, NewArg),
subst(Arg, X, Y, NewArg),
N1 is N - 1,
subst( N1, Term, X, Y, NewTerm )
).