[Date Prev] [Date Next] [Thread Prev] [Thread Next] Date Index Thread Index Search archive:
Date:Mon Nov 2 18:44:26 1999 
Subject:news-relayFrom: pop@edlab.cs.umass.edu (Robin Popplestone) 
From:PP User 
Volume-ID:991102.04 

Here is a version of Abelson and Sussman's Scheme version of unification
Note that the copyright remains with A and S, since it's but a translation.


/* let.p Robin Popplestone MAY 1996 The following macro definition
provides a "let" statement.

let
    jane = 7,
    joy = 6
in
    jane+joy
endlet

which would seem reasonably natural to users of Scheme and ML, controls
the scope of the declared variables, and avoids the perennial pitfall
of "lvars" namely that you can easily write:

   lvars jane = 4; joy = 34;

when you mean

   lvars jane = 4, joy = 34;

(the kind of feature regarded as a virtue in C...).

/* (C) Copyright, University of Massachusetts, June 1994, All Rights Reserved
 *
 * This program carries no warranty.
 *
 * send bug reports and suggestions to pop@cs.umass.edu
 *

This program may reproduced for  academic and experimental purposes,  provided
the above attribution is preserved and extended as appropriate.

Commercial rights are reserved.

*/

====================================================================
*/

;;; let.p                   Robin Popplestone, spring 1995

define lconstant check_no_semicolon();
    lvars l,L = proglist, d=0;
    repeat; hd(L) -> l;
        if member(l, vedopeners) then d+1->d
        endif;
        if l == ";" and d == 0  then
            mishap('";" encountered in let binding', [^proglist]);
        endif;
        if member(l,vedclosers) then d-1->d;
        endif;
        if l = "in" and d==0 then ";" -> hd(L); quitloop;
        endif;

        tl(L) -> L;

    endrepeat;
enddefine;

define macro let;
  check_no_semicolon();
  "lblock", "lvars"
enddefine;

define macro endlet;
  "endlblock"
enddefine;

"let"::vedopeners -> vedopeners;
"endlet"::vedclosers -> vedclosers;
"in"::vedbackers->vedbackers;


define AnExample(p); unless p then mishap('Example failed',[]) endunless;
enddefine; define assoc_scm(x,l); if null(l) then false elseif front(hd(l))
= x then hd(l) else assoc_scm(x,tl(l)) endif enddefine;

2.1 Distinguishing Variables from Constants
-------------------------------------------


The range of possible constants can include Scheme symbols, so we have
a problem in distinguishing between a symbol acting as a constant and
as variable, just as we did in Scheme itself. In the logic paradigm,
however, rather than use quotation to distinguish symbols acting as
constants, it is more common to use some other convention. In the Lisp
community it has been common to use a prefixed question mark. In this
implementation we will treat a variable as a list [? v ) where v is
(conventionally) a symbol.

define var_?(e);
    ispair( e) and hd(e) = "?"
enddefine;




This means that any Scheme atom is a ________constant in our representation
of logic:

define constant_? =  atom;
enddefine;



and we may use equal? to determine if two constants are the same:

define =_constant_? =  nonop =;
enddefine;



3 Implementing the Unification Algorithm
----------------------------------------


3.1 Binding variables to values; making frames
----------------------------------------------


To unify two terms t1 and t2 we must ___________determining_ _a_ _______mapping_ _f_ ____from_ _________variables
__to_ _________constants_ _____under_ _____which f(t1) = f(t2). We will refer to our representation
of such a mapping as a _____frame. However let us take an abstract approach,
and suppose that we have the following operations:

make_binding      Makes a "binding" that is a pair associating
                  a variable with a value.

binding_in_frame  Finds a binding for a given variable in a frame

binding_value     Extracts the value component of a binding.

extend            Adds a new variable-value binding to a frame.



We may implement these operations using a-lists for frames as follows:


define make_binding = conspair;
enddefine;

define binding_in_frame = assoc_scm;
enddefine;

define binding_value = back;
enddefine;

define extend(var, val, frame);
    make_binding(var, val) :: frame
enddefine;





3.2 The function unify
----------------------


To unify two terms p1 and p2 we write the function unify. It takes
3 arguments, p1, p2 frame. Here the frame serves the same role as a
frame in the environment in our Scheme interpreter _ it remembers what
variables have been bound to and so avoids having to perform excessive
substitution. However in the case of unification there are extra possibilities
that have to be taken into account

    # Unification may not be possible. For example if we cannot unify
    2 with 3 To deal with this possibility, we allow the frame to take
    the value false. This should be distinguished from the empty frame
    [) which binds no variables. For example we may unify 2 with 2,
    and the resulting frame is [). Note that here we are depending
    on the use of Scheme being implemented to the IEEE standard in
    which the empty list and the false truth value are distinct.
    # Variables may be bound to variables. Thus [? x) unifies with [?
    y) as specified in the frame which we can think of as ( ([? x)
    . [? y))). but which will actually print as ( ([? x) ? y)).


So let us consider the unification function. Essentially there are
the following cases to consider

    # [1] frame may be false. This case can arise because the unification
    has already failed but the code has not yet checked that this had
    happened. Checking here simplifies case [5] below. (As well as
    it being good practice to check for all types of values an argument
    can have)
    # [2] p1 may be a variable. This case turns out to be quite complicated,
    since p1 may actually be bound in the frame and so is not free
    to be rebound. For example if we try to unify (+ [? x) [? x)) with
    (+ 2 3) we will first unify the variable [? x) with 2 obtaining
    the frame (([? x) . 2)). Then we will need to unify [? x) with
    3. At this point the unification must fail. So we "pass the buck"
    to an auxiliary function unify_var.
    # [3] p2 may be a variable. Again we call unify_var, but with its
    arguments reversed. This means that the first argument of unify_var
    is always guaranteed to be a variable, simplifying its implementation.

    # [4] If p1 is a constant, then p2 must also be a constant, and it
    must be the ____same constant.
    # [5] If p2 is a constant then p1 could not have been a constant,
    so unification fails.
    # [6] Otherwise both p1 and p2 must be lists which represent complex
    terms and not variables. So we call unify recursively on the hd
    and cdr of both. However we have to treat frame as a kind of accumulator,
    in much the same way as we needed an accumulator when we were making
    a list of the nodes of a ordered tree. So we call unify on the
    hd's of the two terms [6.2] obtaining a new frame which embodies
    any variable bindings necessary to unify the cdr's, and then we
    pass this frame to another call of unify [6.1] which proceeds to
    unify the cdr's, keeping the unification consistent with the bindings
    made in the hd's.


define unify(p1, p2, frame);
    if     not(frame) then false                           ;;;[1]

    elseif var_?(p1)     then unify_var(p1, p2, frame)     ;;;[2]

    elseif var_?(p2)     then unify_var(p2, p1, frame)     ;;;[3]

    elseif constant_?(p1) then                             ;;;[4]
        if constant_?(p2) then
            if =_constant_?(p1, p2) then frame
            else false
            endif
        else false
        endif

    elseif constant_?(p2) then false                       ;;;[5]
    else                                                   ;;;[6]
        unify (
            tl(p1),                                        ;;;[6.1]
            tl(p2),
            unify(                                         ;;;[6.2]
                hd(p1),
                hd(p2),
                frame))
    endif
enddefine;



3.3 The unify_var function unifies a variable with a term
---------------------------------------------------------


Now we come to the definition of unify_var. There is a subtlety here
that needs to be dealt with. What happens if we try to unify a variable
with a term that _______already_ ________contains_ ___the_ ________variable. Suppose we want to
unify a variable [? x) with the term (f [? x)); we can only do this
if we systematically replace the variable [? x) with (f [? x)) everywhere
in our terms, including ______inside the term (f [? x)). But this involves
an infinite amount of work generating an infinite term. This kind of
thing can happen if we try to unify:

    [g [? x] [f [? x]]]
    [g [? y] [? y]]



Standard logic does not allow such a circular substitution to happen,
and so we have to put an ______occurs_ _____check into unify_var which makes sure
that we do not unify a variable with a term in which it occurs. [It
should be noted that the best known computer language based on the
Logic Paradigm, Prolog, does not perform the occurs check because it
is computationally expensive].

So let us now consider the definition of unify_var. Firstly [1] we
have to allow for the possiblity that val is a variable, and indeed
is the same variable as var. In this case, unification is trivial with
the existing frame [1.1].

Otherwise we find the value_cell for the value of the variable in the
frame [2].

If [3] it does not exist, then we may bind var to be val in a new frame
made by extend [3.2]. However this is only legal if the "occurs_check"
implemented by freefor_? succeeds [3.1] - otherwise unification fails
[3.3].

If [4] the variable var ___was bound in the frame, then we call unify
to see if the value of var in frame and val can be unified.

define unify_var(var,val,frame);
    if  var = val then                                      ;;;[1]
        frame                                               ;;;[1.1]
    else
        let value_cell =  binding_in_frame(var, frame)      ;;;[2]
        in
            if not(value_cell) then                         ;;;[3]
                if freefor_?(var, val, frame)                ;;;[3.1]
                then extend(var, val, frame)                ;;;[3.2]
                else    false                               ;;;[3.3]
                endif
            else
                unify (binding_value(value_cell),           ;;;[4]
                    val,
                    frame)
            endif
        endlet
    endif
enddefine;




3.4 Implementing the occurs check with freefor_?
------------------------------------------------


Finally let us look at freefor_?. We write this with an internal recursive
function freewalk to save us the trouble of passing more than one argument.
So, we are asking, is expr free of occurrences of var? We recurse through
freewalk, examining the various possible types of the expression e.


    # [1] If e is a constant, then var does not occur in it!
    # [2] If e is a variable, and is the same variable as var then [2.1]
    var does occur in e, so we return false. Othewise, we look up e
    in frame [2.2]. If e is not bound, then we conclude that e is free
    of var, so we return true [2.3]. If it is bound, then we look to
    see if var occurs in the value to which e was bound [2.4].
    # [3] Otherwise e must be a complex term, so we look to see if the
    hd of e is free of var. If it is, the cdr of e must also be free
    of e.
    # [4] Finally, if (hd e) was not free of var then e itself is not
    free of var.


define freefor_?(var, expr, frame);
    define freewalk(e);
        if  constant_?(e) then true                         ;;;[1]
        elseif var_?(e) then                                    ;;;[2]
            if  var = e then false                        ;;;[2.1]
            else
                let b = binding_in_frame(e, frame)    ;;;[2.2]
                in
                    if not(b) then true                       ;;;[2.3]
                    else freewalk(binding_value(b))   ;;;[2.4]
                    endif
                endlet
            endif
        elseif freewalk(hd(e)) then
            freewalk (tl(e))                          ;;;[3]
        else false
        endif
    enddefine;
    freewalk(expr)
enddefine;




3.5 Examples of the use of unify
--------------------------------


And here are some examples of the use of unify and associated functions.


AnExample(
    freefor_?( [? x],  [f [? x]], []) = false);

AnExample(
    freefor_?([? x],  [f [? x]], []) = false);

AnExample(
    unify("Liz", "Phil", []) = false);


AnExample(
    unify([+ a b], [+ a b], []) = []);

AnExample(
    unify([+ a 2], [+ a b], []) = false);

AnExample(
    unify([+ [? a] 4], [+ b 4], []) =
        [% conspair([? a],"b") %]);

AnExample(
    unify([+ [? a] [? a]], [+ b b], []) =
        [% conspair([? a],"b") %]);


AnExample(
    unify([+ [? a] [? a]], [+ 4 3], []) =
    false)

AnExample(
    unify( [+ [? a] 7],  [+ 4 [? b]], []) =
        [%
            conspair([? b],7),
            conspair([? a],4)
        %]);



**** BAD HTML:  Opening tag "body" mismatched with closing tag "html"



++++++++++++++++++++++ANCHOR+++INFO+++++++++++++++++++++++++++++++++++
SOURCE 'unify_in_pop.html'
HREF
NAME
'unification' = 127
IMG
END