CPS 352/543, 430/542 Lecture notes: Introduction to PROLOG



Coverage:
    (CPS 352/543) [COPL] §§16.4-16.8 (pp. 624-650), [PLPP] §§12.4-12.5 (pp. 552-568), and
    (CPS 430/542) [FCDB] §§10.1-10.3 (pp. 463-492)


Logic programming

  • tries to make programming a specification (of solution) activity (called declarative programming), but it falls shorts
  • PROLOG (PROgramming LOGic) is a declarative programming language


Formalism gone awry

  • what is the problem with implementing resolution in a computer system?
  • how should be search the database: top-down, bottom-up, or neither?
  • the goal {} ⊂ legs(horse, 4) led to 2 sub-goals: {} ⊂ mammal(x) ∧ arms(x,0)
  • in which order should the system try to prove the sub-goals? left-to-right or right-to-left or neither?
  • in this case, the end result (true) is the same, but in other cases in might make a difference (e.g. (see proof tree on p. 559 of [PLPP]),
      ancestor(x,y) ⊂ parent(x,z) ∧ ancestor(z,y).
      ancestor(x,x).
      parent(amy,bob).

      {} ⊂ ancestor(x,bob).
    )
  • therefore: order in which
    • the database, and
    • sub-goals are searched
    is significant

  • an implementation must use a fixed search strategy, and the programmer must be aware of these (unsettling)
  • questions
    • how to search the database? top-down or bottom-up or neither?
    • how to search the sub-goals? left-to-right or right-to-left or neither?
  • PROLOG search its database top-down and searches sub-goals left-to-right (i.e., using depth-first-search)
  • do not confuse backward chaining with bottom-up search
  • this violates the defining principle of a declarative language, that is that the programmer need worry only about the logic and leave the control (inference methods used to prove an answer) up to the system (resolution comes for free with PROLOG, programmer need not program it!)
  • we have a pure functional programming language (Haskell), can we have a pure logic programming language?
  • the answer is yes, but it would be far too inefficient to be practical
  • holy grail of logic programming: original goal of logic programming was to make programming a specification activity (i.e., declarative programming!)
  • pure logic programming is non-deterministic; programmers should not have to impart control flow
  • therefore, PROLOG falls short


Summary

If we have a goal Q, to prove Q, PROLOG must either
  • find Q as a fact in the database, or
  • find Q as a sequence of propositions such that
      P2P1.
      P3P2.
      ...
      QPn.


Applications of logic programming

  • logical reasoning
  • problem solving
    • cryparithmetic problems (e.g.,
      SEND
      +MORE
      ------------
      MONEY
      )
    • GRE analytical problems
    • puzzles
  • artificial intelligence
  • specification verification (or rapid prototyping)
  • equational logic programming languages (e.g., OBJ3 or Equation Interpreter Project)


Essential PROLOG programming

  • PROLOG programs are built from terms; a term is either a constant, variable, or structure
  • assert facts and rules
    • constants and predicates must start with a lower case letter
    • have no intrinsic semantics; they are whatever you want them to mean
  • ask questions; variables must start with a CAPITAL LETTER or _
  • simple rules; head and body of rule
  • in PROLOG, all functions are predicates (i.e., return true or false; have to `fake' returns of other types by passing additional arguments)
  • use period (.), not semicolon (;)
  • two ways of consulting a database (i.e., compiling a PROLOG program):
    • use the consult predicate (i.e., consult('filename')., or
    • use [filename].)
    • ?- consult('movies.pl').
      ?- [movies]. % abbreviated form of the above
          
  • use make. to re-consult a file (in SWI-PROLOG)
  • use n or ; character to get next solution
  • comments
    • % introduces a comment until the end of a line
    • C style comments /* ... */ are also permitted, but unlike in C, in PROLOG these comments can be nested
  • halt. or EOF character (e.g., ctrl-D on UNIX, ends session with PROLOG)
  • use predicate protocol/1 to log your session with PROLOG (e.g., protocol('diary').)
  • backtracking
  • tracing and trace/0: allows user to trace the resolutions process, including instantiations, as PROLOG tries to satisfy a goal
  • outputting text
    • write
    • writeln
    • nl (newline)
  • call/1 is the PROLOG analog of eval in Scheme
  • add the following goal
    set_prolog_flag(toplevel_print_options,[quoted(true), portray(true),
    max_depth(0)]).
    
    to a program to prevent PROLOG from abbreviating results with ellipses. Here the value of max depth indicates how deep into the list it will print. By default it is 10; if it is set to 0, then the printing depth limit is turned off.

  • Kowalski's classic formulation of PROLOG strategy: algorithm = logic + control
  • contrast with Wirth's formulation of imperative programming: programs = algorithms + data structures


Unification examples

    (ref. [PLPP])
    me = me.
    
    me = you.
    
    me = X.
    
    f(a,X) = g(Y,b).
    
    f(a,X) = f(Y,b).
    
    f(X) = g(X).
    
    gcd(U,0,U).
    gcd(U,V,W) :- V \=0, R is U mod V, gcd(V,R,W).
    


Lists in PROLOG

  • uses brackets (like ML and Haskell, but not LISP) to specify lists
  • list construction/decomposition in PROLOG vs. Haskell
    -----------------------
    PROLOG          Haskell
    -----------------------
    [X|Y]           X:Y
    .(X,Y)          X:Y
    [X,Y]           X:Y:nil
    [X]             X:nil
    [X,Y|Z]         X:Y:Z
    [X,Y,Z|W]       X:Y:Z:W
    -----------------------
    
  • the following expressions are nonsense
    X|Y             
    [X|Y,Z]         
    [X|Y|Z]     
    


Simple PROLOG database

    animal(cow).
    animal(dog).
    animal(ramsay).
    animal(rogerrabbit).
    animal(yak).
    animal('Emu').
    
    likes(larry,lucy).
    likes(lucy,apples).
    likes(larry,larry).
    
    cpscourse(cps352).
    cpscourse(cps543).
    cpscourse(cps430).
    wtcourse(wt150).
    wtcourse(wt151).
    challenging(_x) :- cpscourse(_x).
    easy(X) :- wtcourse(X).
    easy(cps352).
    
    ihave([pencil,pen,watch]).
    ihave([cps352,cps430,cps444,cps350]).
    ihave([itall]).
    ihave([[toyota,2006,corolla],[2008,honda,civic]]).
    ihave([book,[pen1, pen2],[newdollabill]]).
    ihave(itall).
    


Simple session with PROLOG

    ?- consult(first).
    % animal compiled 0.00 sec, 936 bytes
    
    Yes
    ?- animal(X).
    
    X = cow 
    
    Yes
    ?-  animal(X).
    
    X = cow ;
    
    X = dog ;
    
    X = ramsay ;
    
    X = rogerrabbit ;
    
    X = yak ;
    
    X = emu ;
    
    No
    ?- animal(WhatIwant).
    
    WhatIwant = cow ;
    
    WhatIwant = dog ;
    
    WhatIwant = ramsay ;
    
    WhatIwant = rogerrabbit ;
    
    WhatIwant = yak ;
    
    WhatIwant = emu ;
    
    No
    ?- animal(whatiwant).
    
    No
    ?- animal(whatIwant).
    
    No
    ?- animal(X), animal(Y).
    
    X = cow
    Y = cow ;
    
    X = cow
    Y = dog ;
    
    X = cow
    Y = ramsay ;
    
    X = cow
    Y = rogerrabbit ;
    
    X = cow
    Y = yak ;
    
    X = cow
    Y = emu ;
    
    X = dog
    Y = cow ;
    
    X = dog
    Y = dog ;
    
    X = dog
    Y = ramsay ;
    
    X = dog
    Y = rogerrabbit ;
    
    X = dog
    Y = yak ;
    
    X = dog
    Y = emu ;
    
    X = ramsay 
    Y = cow ;
    
    X = ramsay
    Y = dog ;
    
    X = ramsay
    Y = ramsay ;
    
    X = ramsay 
    Y = rogerrabbit ;
    
    X = ramsay
    Y = yak ;
    
    X = ramsay
    Y = emu ;
    
    X = rogerrabbit
    Y = cow ;
    
    X = rogerrabbit
    Y = dog ;
    
    X = rogerrabbit
    Y = ramsay ;
    
    X = rogerrabbit
    Y = rogerrabbit ;
    
    X = rogerrabbit
    Y = yak ;
    
    X = rogerrabbit
    Y = emu ;
    
    X = yak
    Y = cow ;
    
    X = yak
    Y = dog ;
    
    X = yak
    Y = ramsay ;
    
    X = yak
    Y = rogerrabbit ;
    
    X = yak
    Y = yak ;
    
    X = yak
    Y = emu ;
    
    X = emu
    Y = cow ;
    
    X = emu
    Y = dog ;
    
    X = emu
    Y = ramsay ;
    
    X = emu
    Y = rogerrabbit ;
    
    X = emu
    Y = yak ;
    
    X = emu
    Y = emu ;
    
    No
    ?- animal(X), animal(Y), X\=Y.
    
    X = cow
    Y = dog ;
    
    X = cow
    Y = rabbit ;
    
    X = cow
    Y = rogerrabbit 
    
    ?- likes(larry,X), likes(X,apples).
    
    X = lucy ;
    
    ?- cpscourse(X).
    
    X = cps534 ;
    
    X = cps430 ;
    
    X = cps352 ;
    
    No
    ?- easy(X).
    
    X = cps352 ;
    
    X = wt150 ;
    
    X = wt151 ;
    
    No
    
    ?- ihave(X).
    
    X = [pencil,pen,watch] ;
    
    X = [cps352,cps430,cps444,cps350] ;
    
    X = [itall] ;
    
    X = [[toyota,1998,corolla],[1997,honda,civic]] ;
    
    X = [book,[pen1,pen2],[newdollabill]] ;
    
    X = itall ;
    
    No
    ?- ihave([X|Y]).
    
    X = pencil
    Y = [pen,watch] ;
    
    X = cps352
    Y = [cps430,cps444,cps350] ;
    
    X = itall
    Y = [] ;
    
    X = [toyota,1998,corolla]
    Y = [[1997,honda,civic]] ;
    
    X = book
    Y = [[pen1,pen2],[newdollabill]] ;
    
    No
    ?- ihave([X,Y|Z]).
    
    X = pencil
    Y = pen
    Z = [watch] ;
    
    X = cps352
    Y = cps430
    Z = [cps444,cps350] ;
    
    X = [toyota,1998,corolla]
    Y = [1997,honda,civic]
    Z = [] ;
    
    X = book
    Y = [pen1,pen2]
    Z = [[newdollabill]] ;
    
    No
    ?- ihave([X,Y]).
    
    X = [toyota,1998,corolla]
    Y = [1997,honda,civic] ;
    
    No
    ?- ihave([X]).
    
    X = itall ;
    
    No
    ?- ihave([X,Y,Z]).
    
    X = pencil
    Y = pen
    Z = watch ;
    
    X = book
    Y = [pen1,pen2]
    Z = [newdollabill] ;
    
    No
    ?- halt.
    


Simple control

  • ancestor
    • order of clauses matter
    • left recursion is bad, since PROLOG uses DFS!
  • mutual recursion is bad: doesn't cause stack overflow, but infinite loop nevertheless
  • backtracking to find alternate solutions
  • % ref. [PLPP] pp. 558-559
    ancestor(X,Y) :- parent(X,Z), ancestor(Z,Y).  % clause 1
    ancestor(X,X).                                % clause 2
    parent(amy,bob).
    
    analyze search tree:



    (ref. [PLPP] Fig. 12.2, p. 559)
    /* in ancestor(X,Y), Y is meant to be the ancestor of X */
    
    /* order of predicate definitions matters
       beware of left recursion, as written below
       because PROLOG uses depth-first search
     */
    parent(sallie,amy).
    parent(amy,marc).
    
    ancestor(X,Y) :- parent(X,Y).
    /*
    ancestor(X,Y) :- ancestor(X,Z), parent(Z,Y).
     */
    /* left recursion is bad */
    /* why doesn't left recursion cause a
       problem in reverse?
     */
    
    ancestor(X,Y) :- parent(Z,Y), ancestor(X,Z).
    /* the fix
    */
    
    /* have not exhausted stack,
       rather an infinite transfer of control
     */
    niceday(X) :- classletsoffearly(X).
    classletsoffearly(X) :- niceday(X).
    


List codes written in class

    isempty([]).
    
    islist([]).
    /* only one of the following is required, the first is preferred */
    islist([_|_]).
    islist([_|T]) :- islist(T).
    
    /* only one of the following is required, the first is preferred */
    cons(H,T,[H,T]).
    cons(H,T,L) :- L = [H|T].
    
    /* member is built-in */
    member(E,[E|_]).
    member(E,[_|T]) :- member(E,T).
    


Using append as a primitive to construct some simple list predicates

    /* predicate to append two lists; */
    /* first two are the inputs, last is the appended list */
    
    /* this predicate has a minor bug; */
    /* it does not prune duplicates (see below) */
    /* fix it */
    append([],L,L).
    append(L,[],L).
    append([H|T],Y,[H|W]) :- append(T,Y,W).
    
    /* predicate to check if X is a member of list List */
    member(E,L) :- append(_,[E|_],L).
    
    
    
    /* predicate to check if X is a sublist of Y */
    sublist(X,Y) :- append(_,X,W), append(W,_,Y).
    
    /* predicate to triple a list
       given [3] produce [3,3,3] as answer
       L when tripled gives list LLL */
    triple(L,LLL) :- append(L,L,LL), append(LL,L,LLL).
    
    /* predicate to reverse a list
       give X as input, Y is the reversed X */
    reverse([],[]).
    /* why isn't left recursion a problem here? */
    reverse([H|T],RL) :- reverse(T,RT), append(RT,[H],RL).
    
    /* predicate to do bubblesort
       X when bubblesorted gives Y */
    bubblesort(L,SL) :- append(M,[A,B|N],L),
                        A > B,
                        append(M,[B,A|N],S),
                        %bubblesort(S,SL).
                        bubblesort(S,SL), !.
    bubblesort(L,L).
    /* fix this code yourself, so as to
       not give more answers after the correct one */
    


Paths

    /* edge(X,Y) means there is a directed edge from X to Y */
    
    edge(a,b).
    edge(b,c).
    edge(c,a).
    
    /* path(X,Y) is true when there is a
    directed path from X to Y */
    
    /* have a third argument that keeps a running tally of nodes visited so far */
    
    path(X,X,_).
    path(X,Y,T)  :- edge(X,Z),
                    notamember(Z,T),
                    append([Z,T,T2),
                    path(Z,Y,T2).
    /* we can go from X to Y through Z only
    if Z was not already visited in T */
    
    notamember(E,[]).
    notamember(E,[H|T]) :- E \= H, notamember(E,T).
    


Analogs to relational databases

(primarily for CPS 430/542)


    % relation, called predicate in PROLOG
    movies1(roger_rabbit,1990,123,paramount).
    movies1(get_shorty,1966,122,fox).
    
    movies2(get_shorty,1966,122,fox).
    
    % union
    moviesU(X,Y,Z,W) :- movies1(X,Y,Z,W).
    moviesU(X,Y,Z,W) :- movies2(X,Y,Z,W).
    
    % intersection
    moviesI(X,Y,Z,W) :- movies1(X,Y,Z,W), movies2(X,Y,Z,W).
    
    % difference
    moviesD(X,Y,Z,W) :- movies1(X,Y,Z,W), not(movies2(X,Y,Z,W)).
    
    % projection
    title(T) :- movies1(T,_,_,_).
    
    % selection
    new_movies(T,Y,L,fox) :- movies1(T,Y,L,fox), L >= 121.
    
    % selection followed by a projection
    new_movies2(T) :- movies1(T,_,L,fox), L >= 121.
    
    % or
    new_movies2(T) :- movies1(T,_,L,S), S = 'fox', L >= 121.
    
    % theta-join
    fox_stars(T,Y,L,fox,N) :- movies(T,Y,L,fox), actors(N,T,fox).
    
    % natural join
    movies_aug(T,Y,L,S,N) :- movies(T,Y,L,S), stars(N,T,S).
    


Imparting more control in PROLOG: the cut operator (!)

  • ! is called cut; it is the goto of PROLOG; use it with great precaution
  • cut (!) always evaluates to true (e.g.,
    ?- !.
    Yes
    
    )
  • fail always evaluates to false (e.g.,
    ?- false.
    No
    
    )
  • uses of cut
    • preventing consideration of alternate solutions
    • preventing multiple solutions from being produced
    • reduce number of branches in search tree to pursue
    • 'freezing' parts of solutions
    all really one in the same

  • example
    /* cut prevents consideration of alternate
       solutions by freezing parts of current solution.  */
    juice(apple).
    juice(grape).
    juice(orange).
    
    /* execute the above code with the following goals:
    ?- juice(X).
    ?- juice(X), juice(Y).
    ?- juice(X), !, juice(Y) */
    
    juice(apple).
    juice(grape) :- !.
    juice(orange).
    


More cut examples

  • example (ref. [PLPP] pp. 561-562):
    ancestor(X,Y) :- parent(X,Z), ancestor(Z,Y).
    ancestor(X,X).
    parent(amy,bob).
    
    /* goal is ancestor(X,bob) */
    
    % try these various combos:
    
    ancestor(X,Y) :- parent(X,Z), !, ancestor(Z,Y).
    ancestor(X,X).
    parent(amy,bob).
    




    (ref. [PLPP] Fig. 12.4, p. 561)
    ancestor(X,Y) :- !, parent(X,Z), ancestor(Z,Y).
    ancestor(X,X).
    parent(amy,bob).
    




    (ref. [PLPP] Fig. 12.4, p. 561)
    ancestor(X,Y) :- parent(X,Z), ancestor(Z,Y).
    ancestor(X,X) :- !.
    parent(amy,bob).
    
  • member example:
    /* cut here prevent member from finding all occurrences */
    member(E,[E|_]) :- !.
    member(E,[_|T]) :- member(E,T).
    
    member(E,[1,2,1,7]).
    E = 1 ;
    
    No
    
  • squarelist example:
    /* squarelist takes a list and squares every integer element of the list;
       if an element is not an integer,
       it is simply inserts the element as is into the result list, the second argument
     
       here, cut is serving two purposes:
          - fix rule 2 when you know A is an integer 
          - skip all subsequent rules (in this case, rule 3) */
    squarelist([],[]).
    squarelist([A|B],[C|D]) :- integer(A), !,
                               C is A*A,
                               squarelist(B,D).
    squarelist([A|B],[A|D]) :- squarelist(B,D).
    
  • practice with cuts from [PPFC]
    /* more practice with cuts;
       determine what each of back1, back2, and back3 will print */
    back1 :- example(X),
             d(Y),
             write(X), write(Y), nl,
             fail.
    
    back2 :- example(X), !,
             d(Y),
             write(X), write(Y), nl,
             fail.
    
    back3 :- example(X),
             e(Y),
             write(X), write(Y), nl,
             fail.
    
    example(1).
    example(2).
    
    d(1).
    d(2).
    d(3).
    
    e(1) :- !.
    e(2).
    


Towers of Hanoi in PROLOG

    /* move n disks from peg A to peg B using peg C as intermediary;
       requires (2^n)-1 moves, where n is the number of discs */
    
    /* does this cut serve any useful purpose? */
    move(0,_,_,_) :- !.
    move(N,A,B,C) :- M is N-1,
                     move(M,A,C,B),
                     step(A,B),
                     move(M,C,B,A).
    
    step(A,B) :- write('Move one disc from peg '),
                 write(A),
                 write(' to peg '),
                 write(B),
                 writeln('.'),
    
    /* Towers of Hanoi is an exponential-time algorithm.
       Orders of Growth: if we ran an exponential-time algorithm with a
       data set of size n=100 on a computer which performed
       1 billion operations per second, we would have to
       wait for approximately 4 x 10^11 centuries for the
       code to finish running!  */
    


Math in PROLOG

    gcd(U,0,U).
    gcd(U,V,W) :- V \=0, R is U mod V, gcd(V,R,W).
    
    factorial(0,1).
    factorial(X,Y) :- X > 0,
                      Z is X-1,
                      factorial(Z,M),
                      Y is M*X.
    
    sum(0,0).
    sum(X,Y) :- X > 0,
                Z is X-1,
                sum(Z,M),
                Y is M+X.
    


Negation in PROLOG

  • is the not/1 predicate in PROLOG a logical NOT?
  • exercise care when using it
  • its use can produce counter-intuitive results
  • mother(mary).
    
    ?- mother(X).
    X = mary
    
    true.
    
    ?- not(mother(X)).
    false % this is not saying 'there are no mothers' 
    
    ?- not(not(mother(X))).
    X = _G157
    
    starts with the innermost clause and succeeds with X = mary,
    the moment the clause becomes false, the instantiation is released
    
    ?- not(not(mother(mary))).
    true.
    
    ?- not(X=0) % returns no, without binding X 
    false.
    
    /* in logic the order should not matter */
    ?- X=0, not(X=1). % instantiates X to 0 */
    ?- not(X=1), X=0. % fail at first sub-goal without binding X to 0. why?
    
    ?- not(not(X=1)), X=0.
    X=0.
    
    ?- not(X=1), not(X=0).
    false.
    
    moral of the story: not in PROLOG is not a logical NOT


Reflective predicates

Recall, PROLOG programs are built from terms. A term is either a constant, variable, or structure.
  • assert/1 or assertz/1: adds a fact to the end of the database
  • asserta/1: adds a fact to the beginning of the database
  • retract/1: removes a fact from the database
  • % indicates that car is a dynamic predicate
    :- dynamic car/1.
    
    car(honda).
    car(toyota).
    
    ?- car(CAR).
    
    CAR = honda ;
    
    CAR = toyota ;
    
    No
    ?- asserta(car(ford)).
    
    Yes
    ?- assertz(car(bmw)).
    
    Yes
    ?- retract(car(honda)).
    
    Yes
    ?- car(CAR).
    
    CAR = ford ;
    
    CAR = toyota ;
    
    CAR = bmw ;
    
    No
    ?- 
    
  • assert and retract can be used to build a tic-tac-toe program
  • clause/2: matches the head and body of an existing clause in the database; can be used to implement a metacircular interpreter (i.e., an implementation of call/1 [PLP] p. 578)
  • var(Term): succeeds if Term is currently a free variable
  • nonvar(Term): succeeds if Term is currently not a free variable
  • ground(Term): succeeds if Term holds no free variable


Impurities: mismatch between LOGIC and PROLOG

    FOPLLP (PROLOG)
    any form of proposition possible restricted to Horn clauses
    order in which sub-goals are searched insignificant order in which sub-goals are searched significant (l-to-r)
    order in which clauses are searched insignificant order in which clauses are searched significant (top-down)
    logical NOT NOT as failure (due to unification)

    in summary, `there are aspects of predicate calculus that PROLOG cannot capture and there are aspects of PROLOG (e.g., its imperative and database manipulating features) that have no analogues in predicate calculus' [PLP1] p. 642

    another impure feature of PROLOG: closed world assumption; PROLOG is a true/fail system, like our legal system, rather than a true/false system [COPL]


Problems with PROLOG

(these are all somewhat interrelated)
  • to be purely declarative, programmer should not be required to affect control flow for program success
    • consequent of DFS search strategy
    • left recursion often leads to incorrect results

  • closed world assumption (and nonmonotonic reasoning); PROLOG is a true-fail system, not a true-false system [COPL] p. 643. There is no mechanism in PROLOG by which to assert facts assumed to be false. PROLOG relies on pure positive logic. This is another reason why not in PROLOG (see below) is not a logical not. As a result not(P) can succeed simply because PROLOG cannot prove P true.

  • Horn clauses are not expressive enough to capture all knowledge in first-order predicate calculus (e.g., propositions in clausal form with with a disjunction of more than one nonnegated term such as every `every positive natural number is either even or odd'
    even(N) ∨ odd(N) ⊂ natural(N). 
    
    )
  • negation as failure (a reflection of PROLOG's limitation to Horn clauses)
    odd(N) :- natural(N), not(even(N)).
    even(N) :- natural(N), not(odd(N)).
    
  • more negation issues: not(transmission(X,manual)) means ¬ ∃X (transmission(X,manual)) or `there are no cars with manual transmissions' rather than ∃X (¬transmission(X,manual)) or `not all cars have a manual transmission' (example inspired by [PLP] p. 582). As a result, the goal not(transmission(X,manual)) fails even if we have transmission(accord,manual) in our database.

  • the occurs-check problem


Semantic part of PROLOG interpreter

(an implementation of the built-in call/1) (ref. [EBG=PE])
    prolog(Leaf) :- clause(Leaf,true).
    prolog((Goal1, Goal2)) :- prolog(Goal1), prolog(Goal2).
    prolog(Goal) :- clause(Goal,Clause), prolog(Clause).
    


References

    [COPL] R.W. Sebesta. Concepts of Programming Languages. Addison-Wesley, Boston, MA, Sixth edition, 2003.
    [EBG=PE] F. van Harmelen and A. Bundy. Explanation-Based Generalisation = Partial Evaluation. Artificial Intelligence, 36(3), 401-412, 1988.
    [FCDB] J.D. Ullman and J. Widom. A First Course in Database Systems. Prentice Hall, Upper Saddle River, NJ, Second edition, 2002.
    [PLP1] M.L. Scott. Programming Language Pragmatics. Morgan Kaufmann, San Francisco, First edition, 2000.
    [PLP] M.L. Scott. Programming Language Pragmatics. Morgan Kaufmann, Amsterdam, Second edition, 2006.
    [PLPP] K.C. Louden. Programming Languages: Principles and Practice. Brooks/Cole, Pacific Grove, CA, Second edition, 2002.
    [PPFC] P. Burna. Prolog Programming A First Course.

Return Home