begin
The begin
expression sequence special form works
much like the expression-sequence construct
(expr1; ...;
exprn)
in ML: it evaluates each expression
passed to it in sequence, and returns the value of the last
expression.
(+ (begin (display "hi") (newline) (display "bye") (newline) 4) 5) ; => 9 (prints "hi", newline, "bye", newline as a side effect)
Previously, for simplicity, we said that special forms like
cond
and let*
take single expressions
for bodies. Actually, most special forms can take expression
sequences in the "body-part" even without using
begin
. For example:
(let* ((a 5) (b (+ a a))) (display b) (+ b b))
case
The case
construct has the syntax
(case testExpr clause1 clause2 ... clausen)
where each clausei
has the
form
((keyi1 ... keyim) exprSeq
Each keyij
is one value to be
matched against. If the value of testExpr
is
equal to any of the keys for a clause, then the expressions in
exprSeq
will be evaluated sequentially and the
last one returned. For example:
(define x 4) (case x ((1 2 3) "one to three") ((4 5 6) "four to six") (else "really big")) ; => "four to six"
This section demonstrates a few more uses of Scheme's dynamic typing that would be cumbersome to simulate in an ML-like language.
Scheme functions take variable numbers of arguments using "vararg" syntax:
(define swap-first-two (lambda (first second . rest) (list-append (cons second (cons first rest)))))
The apply
function applies any function to an list
as an argument:
(apply + '(1 2 3 4)) ; => 10
The map
function takes any number of
lists, and applies its function argument to all elements in
parallel:
(map (lambda (x y) (+ x y)) '(1 2 3 4) '(5 6 7 8)) ; => '(6 8 10 12)
We define my-map
, with the same behavior as the
built-in map
, as follows (we use two helper functions
to collect the heads and tails of a list of lists):
(define (first-members argLists) (if (null? argLists) () (cons (caar argLists) (first-members (cdr argLists))))) (define (rest-members argLists) (if (null? argLists) () (cons (cdar argLists) (rest-members (cdr argLists))))) ;; Definition of my-map (define (my-map f . argLists) (if (null? (car argLists)) () (cons (apply f (first-members argLists)) (apply my-map f (rest-members argLists)))))
In an object-oriented language, an object contains a list of bindings from names to values --- it so happens that these bindings are called fields. A lexically scoped closure also contains a list of bindings --- the captured environment, including the values of locals. Using lambdas and varargs, we can get a sort-of-satisfactory simulation of objects in Scheme:
(define (make-point x y) (lambda (method . args) (case method ((get-x) x) ((get-y) y) ((set-x!) (set! x (car args))) ((set-y!) (set! y (car args))) (else 'invalid-method-error)))) ;; Usage (define my-point (make-point 1 2)) ; => #>procedure ...> (my-point 'get-x) ; => 1 (my-point 'get-y) ; => 2 (my-point 'set-x! 3) ; => #<void> (my-point 'get-x) ; => 3
The syntax for defining objects is somewhat worse than we'd like, but one can use syntax macros.
foldr
that takes a
function of N arguments, a list of N base
cases, and a list of N lists, and folds all the lists
simultaneously.SchemeVal
datatype we defined in earlier notes to
translate our "point object" into ML-code. Your "object lambda"
should take a method name string, plus a list of
SchemeVal
argument values, and return a
SchemeVal.make-colored-point
constructor that
produces a "colored point", which "inherits" the methods defined
in make-point
and adds new methods
get-color
and set-color!
(use symbols
for colors).