CS 334 Lecture 20

CS 334 Lecture 20

Contents:

  1. Semantics of programming languages
    1. Denotational Semantics
  2. Logic programming
    1. History and Goals
    2. Intro to Prolog
      1. Pure prolog
        1. examples
        2. Logical vs procedural reading of PROLOG programs
        3. backtracking
        4. data structures
        5. arithmetic

Semantics of programming languages - continued

Denotational Semantics

Define E : NumExp -> [State -> [[Value x State] + {error}]] by:

    E [[0]]s = (0,s)

    E [[1]]s = (1,s)

    E [[read]](m, i, o) = if (empty i) then error, 
                                        else (hd i, (m, tl i,  o))

    E [[I]](m, i, o) = if m i = unbound  then  error, 
                                          else  (m I, (m, i, o))

    E [[E1 + E2]]s = if (E [[E1]]s = (v1,s1) & E [[E2]]s1 = (v2,s2))  
                                then (v1 + v2, s2)
                                else error

    E [[fn x => E]]s  = fun n in Nat. E [[E]](s[n/x])

    E [[E1 (E2)]]s = E [[E1]]s (E [[E2]]s)
Note difference in meaning of function here from that of operational semantics!

Define B: BoolExp -> [State -> [[Value x State] + {error}]] by:

    B [[true]]s = (true,s)
    
    B [[false]]s = (false,s)
    
    B [[not B]]s = if  B [[B]]s = (v,s') then  (not v, s'), 
                                          else  error
                        
    B [[E1 = E2]]s = if  (E [[E1]]s = (v1,s1)  & E [[E2]]s1 = (v2,s2))  
                               then  (v1 = v2, s2)
                               else error

Define C : Command -> [State -> [State + {error}]] by:

    C [[I := E]]s = if  E [[E]]s = (v,(m,i,o)) then (m[v/I],i,o)
                                                else  error
where m' = m[v/I] is identical to m except the value of I is v.
    C [[output E]]s = if  E [[E]]s = (v,(m,i,o)) then (m,i,v.o)
                                                  else  error
where v.o is the result of attaching v to the front of o.
    C [[if E then C1 else C2]]s = if  B [[B]]s = (v,s')  
                                            then  if  v  then  C [[C1]]s'
                                                         else  C [[C2]]s'
                                            else  error
                                            
    C [[while E do C]]s = if  B [[B]]s = (v,s') 
                                     then if v then if  C [[C]]s' = s''  
                                                    then C [[while E do C]]s''
                                                    else  error
                                               else  s'
                                     else  error
                                   
    C [[C1; C2]]s = if  C [[C1]]s = error then error
                                           else  C [[C2]] ( C [[C1]]s)
End Tiny

Notice that definition of while is a recursive definition.

Thus, if B [[B]] s = True and s' = C [[S]] s, then C[[while B do S]]s = C [[while B do S]]s'

Solution involves computing what is known as least fixed points.

Denotational semantics gained considerable popularity over last 15 years.

Many people consider denotational semantics as most appropriate for studying meaning of language independent of its implementation.

Good for looking at trade-offs between alternate forms of similar constructs.

Which is best?

No good answer, since have different uses.

Complementary definitions. Can be compared for consistency.

Programming language definitions usually still given in English. Formal definitions often too hard to understand or require too much sophistication. Gaining much more acceptance. Now relatively standard intro graduate course in CS curricula.

Some success at using formal semantics (either denotational or operational) to automate compiler construction (similar to use of formal grammars in automatic parser generation).

Semantic definitions have also proved to be useful in understanding new programming constructs.

Logic programming

History and Goals

Competitor to LISP for AI programming in 80's - adopted by Japanese for Fifth Generation Computing Project (PROLOG).

What is logic programming? -

"A constructive proof that for every list L there is a corresponding sorted list S composed of the same elements as L yields an algorithm for sorting a list."

Philosophy is shared by others not working on "logic programming", e.g. Constable at Cornell, Martin-Löf in Sweden, Calculus of Constructions group in France. These groups want to extract (more traditional) program from constructive proofs.

Very-High-Level Languages - non-procedural

State what must be done, not how to do it. Idea is to separate logic from control. (Examples later)

Intro to Prolog

We will look at PROLOG (PROgramming in LOGic), first and most important logic programming language.

Developed in 1972 by Alain Colmerauer in Marseilles.

Text has an excellent introduction to PROLOG. I will presume you have read it and concentrate on examples!

Pure prolog

Relational rather than Functional programming language

Often best to start out as thinking of Prolog in terms of language for working with a data base.

Three types of statements:

Facts (or hypotheses):

    father(albert,jeffrey). 
Rules (or conditions):
    grandparent(X,Z) :- parent(X,Y),parent(Y,Z).    
(read ":-" as "if")

Queries (or goals):

    ?-grandparent(X,jeffrey). 

Should think of language as non-deterministic (or non-procedural). Looks for all answers satisfying predicate.

examples

Example - family.pro

father(john,ralph). 
father(mary,ralph). 
father(ralph,bill). 
  
mother(john,sue). 
mother(mary,sue). 
mother(ralph,joan). 
  
male(john). 
male(ralph). 
male(bill). 
  
female(mary). 
female(sue). 
  
/* RULES */ 
is_mother(M):-mother(Y,M). 
  
parent(X,M):-mother(X,M). 
parent(X,F):-father(X,F). 
  
parents(X,M,F):-mother(X,M),father(X,F). 
  
sister(X,S):-female(S),parents(X,M,F),parents(S,M,F),S\=X. 
  
ancester(X,Y):-parent(X,Y). 
ancester(X,Y):-parent(X,Z),ancester(Z,Y). 

/***********************newsort.pro**************************/ 
 

/********************Selection Sort*******************/ 
  
sel_sort([],[]). 
  
sel_sort(L,[Small|R]) :- 
        smallest(L,Small) ,  
        delete(Small,L,Rest) ,  
        sel_sort(Rest,R) . 
  
/* smallest(List, Small) results in Small being the smallest element in List. */ 
  
smallest([Small],Small) . 
  
smallest([H|T],Small) :- 
          smallest(T,Small) ,  
          Small=<H. 
smallest([H|T],H). 
  
/* delete(Elt,List,Result) has Result as List after deleting Elt. */ 
  
delete(Elt,[],[]) . 
  
delete(Elt,[Elt|T],T) . 
  
delete(Elt,[H|T],[H|NuTail]) :- 
        delete(Elt,T,NuTail) . 
  
/*******************Insertion Sort********************/ 
  
ins_sort([],[]) . 
  
ins_sort([H|T],Sorted) :- 
        ins_sort(T,Rest),  
        insert(H,Rest,Sorted) . 
  
/* insert(Elt,List,Result) - if List is sorted, Result is obtained by putting 
     Elt where it fits in order in List. */ 
  
insert(Elt,[],[Elt]) . 
  
insert(Elt,[H|T],[Elt,H|T]) :- 
        (Elt=<H) . 
  
insert(Elt,[H|T],[H|Sorted]) :- 
        insert(Elt,T,Sorted) . 
  
/* **********Quick Sort************** */ 
  
/* sep(List,Key,Less,More) separates the List into the set of elements less 
   than or equal to Key (in Less) and those greater than or equal to Key 
   (in More) */ 
  
sep([],Key,[],[]) .         
  
sep([H|T],Key,Less,[H|More]) :- 
        (H>Key) ,  
        sep(T,Key,Less,More) . 
  
sep([H|T],Key,[H|Less],More) :- 
        (H=<Key) ,  
        sep(T,Key,Less,More) . 
  
quick_sort([],[]) . 
  
quick_sort([H|T],Sorted) :- 
        sep(T,H,Less,More) ,  
        quick_sort(Less,L_sorted) ,  
        quick_sort(More,M_sorted) ,  
        concat(L_sorted,[H|M_sorted],Sorted) . 
  
/* concat(First, Second, List) results in List = concatenation of First 
and Second. */ 
  
concat([],L,L) . 
  
concat([H|T],L,[H|C]) :- concat(T,L,C) . 

/************************************************
Can take advantage of reversibility since computing with relations rather 
than functions. 
********************Permutations*******************/ 
append([],L,L) . 
  
append([H|T],L,[H|R]) :- append(T,L,R) . 

permute([],[]) . 
  
permute(L,[H|T]) :- 
        append(V,[H|U],L) , 
            /* V,U stand for the part of L before and after H */  
        append(V,U,W) ,  
        permute(W,T) . 
  
***********************************************

Logical vs procedural reading of PROLOG programs

Can read Prolog program as specifying solution (or saying what it necessary to deduce that something is a solution) or as specifying computation.

backtracking

How does a prolog program compute an answer?

Ex.

father(john,ralph). 
father(mary,ralph). 
father(ralph,bill). 
mother(john,sue). 
mother(mary,sue). 
mother(ralph,joan). 
/* RULES */ 
is_mother(M):-mother(Y,M). 
parent(X,M):-mother(X,M). 
parent(X,F):-father(X,F). 
ancester(X,Y):-parent(X,Y). 
ancester(X,Y):-parent(X,Z),ancester(Z,Y). 

?-ancester(X,joan). 
backtracking

First succeeds with X = ralph

later w/X= john, mary

data structures

Operators in Prolog

Usually in prefix, can force to be in infix or postfix and give precedence as well.

Ex. +, - ,*,/

2+3*5 abbreviates +(2,*(3,5))

operations are not evaluated

Better to think of operations as forming tags on values -

Relations

=, \=, <, >, =<, >= (note order of composite relations) are evaluated.

Ex. digit(N):- N>=0,N<10.

Example of using operations.

treesort

tree is either nil or is of form maketree(tree1,X,tree2).

Write program to do treesort! Very much like ML program.

arithmetic

If actually wish to calculate, must use "is".

Ex. area(L,W,A):-A is L*W.

Can only compute A from L,W - not reversible.