Scheme programs consist of expressions, which can be:
2
3.3
#t
+
quotient
(quotient 10 2)
(not #t)
The built-in Scheme list data structure can represent combinations:
(list 'quotient 10 2) ; (quotient 10 2)
(eval (list 'quotient 10 2)) ; 5
In such a language, it is straightforward to write a program that writes a program.
There are two ways to quote an expression:
'(a b)
=> (a b)
`(a b)
=> (a b)
They are different because parts of a quasiquoted expression can be unquoted with ,
(define b 4)
'(a ,(+ b 1))
=> (a (unquote (+ b 1))
`(a ,(+ b 1))
=> (a 5)
Quasiquotation is particularly convenient for generating Scheme expressions:
(define (make-adder n) `(lambda (d) (+ d ,n)))
(make-adder 2) ; (lambda (d) (+ d 2))
Base cases:
Recursive calls:
Base cases:
Recursive calls:
The scheme_eval
function chooses behavior based on expression form:
(<operator> <operand 0> ... <operand k>)
The special forms can all be identified by the first element:
(if <predicate> <consequent> <alternative>)
(lambda (<formal-parameters>) <body>)
(define <name> <expression>)
Any combination that is not a known special form must be a call expression.
(define (demo s)
(if (null? s)
'(3)
(cons (car s) (demo (cdr s))))) ; Special!
(demo (list 1 2)) ; Call expression!
Logical forms are special forms that may only evaluate some sub-expressions.
(if <predicate> <consequent> <alternative>)
(and <e1> ... <en>)
, (or <e1> ... <en>)
(cond (<p1> <e1>) ... (<pn> <en>) (else <e>))
The value of an if
expression is the value of a sub-expression:
'<expression>
is shorthand for (quote <expression>)
'(1 2)
is equivalent to (quote (1 2))
The scheme_read
parser converts '
to a combination that starts with quote
.
The quote
special form evaluates to the quoted expression, which is not evaluated.
(quote (+ 1 2))
evaluates to the three-element Scheme list
(+ 1 2)
A frame represents an environment by having a parent frame.
A frame is an instance of a Frame
object,
which has lookup
and define
methods.
In this interpreter, frames do not hold return values.
y | 3 | |
z | 5 |
x | 2 | |
z | 4 |
Define binds a symbol to a value in the first frame of the current environment.
(define <name> <expression>)
<expression>
<name>
to its value in the current frame
(define x (+ 1 2))
Procedure definition is shorthand of define with a lambda expression.
(define (<name> <formal parameters>) <body>)
(define <name> (lambda (<formal parameters>) <body>))
Lambda expressions evaluate to user-defined procedures
(lambda (<formal-parameters>) <body> ... )
class LambdaProcedure:
def __init__(self, formals, body, env):
self.formals = formals # A scheme list of symbols
self.body = body # A scheme list of expressions
self.env = env # A Frame instance
(lambda (x y) (* x y))
To apply a user-defined procedure, create a new frame in which formal parameters are bound to argument values, whose parent is the env attribute of the procedure.
Evaluate the body of the procedure in the environment that starts with this new frame.
(define (demo s)
(if (null? s)
'(3)
(cons (car s) (demo (cdr s)))))
(demo (list 1 2))