Data Abstraction
Multi-way branches:
Special form CASE with the following syntax:
(case key
(key-list1 consuequent1)
...
(key-listn consuequentn)
(else alternative))
Each key-listi is a list of symbols, numbers,
booleans, or characters.
else clause is optional
Semantics:
- key is evaluated
- its value is compared with the key-list
elements
- the consequent corresponding to the first
matching key-list element is evaluated and its
value returned
- if no match, alternative is evaluated and its
value returned.
If no else clause and no match case behavior is
unspecified......
A case expression can always be transformed into
an equivalent expression using let and cond
(let [(*key* key)]
(cond
[(memv *key* 'key-list1) consequent1]
...
[(memv *key* 'key-listn) consequentn]
[else alternative]))
where the variable *key* does not occur-free
within the consequents or alternative.
memv is like member and uses the appropriate eq?,
=, char=?
let is used so key is evaluated only once.
Records, unions, variant records and
DATA ABSTRACTION
We would like to have structures or records in our
Programming Language.
Unlike arrays and vectors where members are
accessed by position/index, we want to access
elements by NAME.
record facility in scheme:
not a standard feature
(define-record name (field1 ... fieldn))
This automatically/automagically defines:
1. a procedure for creating records of type name
constructor
2. A predicate for identifying records of this
type
3. An accessing procedure for each field
access methods
You need record.ss,
Follow "Useful scheme code" link on main web page
under Resources
Consider
binary trees:
TREE ::== NUMBER | (SYMBOL TREE TREE)
Interior nodes are
(SYMBOL TREE TREE)
let us make a record type called interior
(define-record interior
(symbol left-tree right-tree))
(define tree-1
(make-interior
'foo
(make-interior 'bar 1 2)
3))
Petite Chez Scheme Version 6.0a
Copyright (c) 1998 Cadence Research Systems
> > > #t
> (interior? tree-1)
#t
> (interior->symbol tree-1)
foo
> (interior->right-tree
(interior->left-tree tree-1))
2
>
constructor: make-name
predicate : name?
access : name-fieldi
(define leaf-sum
(lambda (tree)
(cond
[(number? tree) tree]
[(interior? tree)
(+
(leaf-sum (interior->left-tree tree))
(leaf-sum (interior->right-tree tree)))]
[else (error 'leaf-sum "Invalid tree")])))
> (leaf-sum '(a 1 2))
> (leaf-sum (make-interior 'a 1 2))
3
In general a distinct record type can be used to
represent each alternative of a BNF specification
TREE ::== NUMBER | (SYMBOL TREE TREE)
we got a record for interior nodes
one for leaf
(define-record leaf (number))
Can you define leaf-sum for trees with
leaf records?
Variant Records and variant-case
A type that combines two or more other types as
alternatives is called a UNION type
tree is a union of
records of type leaf and interior
A union type all of whose alternatives are records
is called a VARIANT RECORD
We use a lot of variant records in this course so
we combine this type with branching on the type of
record.
Once you know the type, it is also useful to bind
some or all of its field values to variables named
after the fields
THEREFORE create the special form
variant-case
Syntax:
(variant-case record-expression
(name1 flist1 conseq1)
...
(namen flistn conseqn)
(else alternative))
Semantics:
flisti is a list of fields for records of type
namei
Each record type should be distinct, as should
field names within a list
1. Record-expression is evaluated with a resulting
value V
2. if V is not a record of one of namei,
alternative is evaluated
3. Else if V is one of namei
each of the fieldnames in flisti
is bound to the value of the field of V with
the same fieldname
4. Consequent is evaluated withing the region of
these bindings and its value returned.
(define-record leaf (number))
(define-record interior
(symbol left-tree right-tree))
(define leaf-sum2
(lambda (tree)
(variant-case tree
[leaf (number) number]
[interior (left-tree right-tree)
(+ (leaf-sum2 left-tree)
(leaf-sum2 right-tree))]
[else (error 'leaf-sum2 "invalid tree")])))
> (leaf-sum2 (make-interior 'a 1 2))
error
> (leaf-sum2
(make-interior 'a
(make-leaf 1)
(make-leaf 2)))
Otherwise:
(define leaf-sum3
(lambda (tree)
(let ((*record* tree))
(cond
[(leaf? *record*)
(let ((number (leaf->number *record*)))
number)]
[(interior? *record*)
(let
((left-tree
(interior->left-tree *record*))
(right-tree
(interior->right-tree *record*)))
(+ (leaf-sum3 left-tree)
(leaf-sum3 right-tree)))]
[else (error 'leaf-sum3 "Invalid tree")]))))
Remember we want to build an interpreter!
Abstract Syntax and representation with
variant-case
We need to convert BNF (concrete syntax) to
Rules associated with each syntactic component
Easy access to each subcomponent
exp ::== NUMBER
| VARREF
| (lambda (VAR) EXP)
| (EXP EXP)
exp ::== NUMBER
RuleName: lit
NonTerminal: datum
variant-case representation:
lit (datum) ...
The rest
lit (datum)
varref (var)
lambda (formal body)
app (rator rands)
consider
(lambda (x) (f (f x)))
Since scheme already reads characters from
input and gives you lists structures parsing is
real simple.....
We want to parse from list-structure into
variant-case usable representation
(define-record lit (datum))
(define-record varref (var))
(define-record lambda (formal body))
(define-record app (rator rand))
(define parse
(lambda (datum)
(cond
[(number? datum) (make-lit datum)]
[(symbol? datum) (make-varref datum)]
[(pair? datum)
(if (eq? (car datum) 'lambda)
(make-lambda
(caadr datum)
(parse (caddr datum)))
(make-app
(parse (car datum))
(parse (cadr datum))))]
[else (error 'parse "Invalid concrete syntax")])))
concrete -> abstract
abstract -> concrete ??
Sushil Louis
Last modified: Mon Sep 27 14:21:31 PDT 1999