Scheme
https://inst.eecs.berkeley.edu/~cs61a/fa23/articles/scheme-spec/
https://inst.eecs.berkeley.edu/~cs61a/fa23/articles/scheme-builtins/
Scheme Fundamentals
Scheme programs consist of expressions, which can be:
- Primitive expressions:
2,3.3,true,+,quotient, ... - Combinations:
(quotient 10 2),not true, ...
Numbers are self-evaluating
Symbols are bound to values
Call expressions include an operator and 0 or more operands in parentheses
> (+ (* 3
(+ (* 2 4)
(+ 3 5)))
(+ (- 10 7)
6))- Combinations can span multiple lines
- Spacing doesn't matter (indent for making expression clearer)
Examples:
> (+ 1 2 3 4)
10
> (* 1 2 3 4)
24
> (+)
1
> (*)
1
> (quotient 7 5)
1; //
> (modulo 7 5)
2; mod
> +
#[+]
> (number? 3)
#t
> (number? +)
#f
> (zero? (- 2 2))
#t
> (integer? 2)
#t
> (integer? 2.2)
#f- The question mark is part of the name
integer?
Special Forms
A combination that is not a call expression is a special form.
If Expression
> (if <predicate> <consequent> <alternative>)Evalutation:
- Evaluate the predicate expression
- Evaluate either the
consequentoralternativeconsequentas the suite ofifclause in Pythonalternativeas the suite ofelseclause in Python
Boolean Operators
Boolean operators are short circuit operators
> (and <e1> ... <en>)
> (or <e1> ... <en>)
> (not <expr>)Binding Symbols
> (define <symbol> <expression>)Symbols defined in the global enviroment are bound to values in the global frame
New Procedures
> (define (<symbol> <formal parameters>) <body>)Example: Babylonian Square Root
> (define (average x y)
(/ (+ x y) 2))
> (define (square x) (* x x))
> (define (sqrt x)
(define (good-enough? guess)
(< (abs (- (square guess) x)) 0.000000001))
(define (improve guess)
(average guess (/ x guess)))
(define (sqrt-iter guess)
(if (good-enough? guess)
guess; Returning the GUESS if true
(sqrt-iter (improve guess)))); Otherwise keep guessing
(sqrt-iter 1.0)); Return the SQRT_ITER function on oprend 1
> (sqrt 9)
3.0Due to some precision problem, the following code may lead to an infinity recursion.
(define (alt_sqrt x)
(define (update guess)
(if (= (square guess) x)
guess
(update (average guess (/ x guess)))))
(update 1.0))Though it can run fairly well in Python.
Here's another recursion example:
(define (power x)
(if (= x 1) x (* x (power (- x 1)))))Lambda Expressions
Lambda expressions evaluate to anonymous procedures
(lambda (<formal-parameters>) <body>)Two equivlent expressions:
(define (plus4 x) (+ x 4))
(define plus4 (lambda (x) (+ x 4)))An operator can be a call expression too:
((lambda (x y z) (+ x y (square z))) 1 2 3)Printing Values
The native-code interpretors use display instead of print, but in 61A we use print
> (define a 3)
> (display a)
3
> (display 'a)
acond
The cond special form that behaves like if-elif-else statements in Python.
(cond ((<condition1>) (<expression1>))
((<condition2>) (<expression2>))
(...)
(else (<expression3>))); OptionalIf we want the conditional statement to get displayed, the best option is to nest the cond statment inside of the display statement.
(display
(cond ((<condition1>) (<expression1>))
((<condition2>) (<expression2>))
(...)
(else (<expression3>))))begin
The begin special form combines multiple expressions into one expression.
(begin <expression1> <expression2> ... <expressionN>)The interpreter will evaluate each expression or execute each procedure one by one.
Let Expressions
The let special form binds symbols to values temporarily, just for one expression.
(let ((<symbol1> <expression1>) (<symbol2> <expression2>) ... (<symbolN> <expressionN>)) (<procedure>))The following is an example of how let is used in the context:
> (define c (let ((a 3)
(b (+ 2 2)))
(sqrt (+ (* a a) (* b b)))
)
)
> c
5
> a
Exception: variable a is not boundCompound Values
Pairs are built into the Scheme language.
Pairs are created with the cons built-in function, and the elements of a pair are accessed with car and cdr:
> (define x (cons 1 2))
x
(1 . 2)
> (car x)
1
> (cdr x)
2consTwo-argument procedure that creates a linked listcarProcedure that returns the first element of a listcdrProcedure that returns the rest of a listnilThe empty list'()
Recursive lists are also built into the language, using pairs. These lists are like the linked list class in Python.
> (cons 1
(cons 2
(cons 3
(cons 4 nil))))
(1 2 3 4); Rendered this way but is in fact recursive
> (list 1 2 3 4)
(1 2 3 4)
> (define lst (list 1 2 3 4))
> (car lst)
1
> (cdr lst)
(2 3 4)
> (car (cdr lst))
2
> (cons 10 lst)
(10 1 2 3 4)
> lst
(1 2 3 4); Not mutatedWhether a value is a list can be determined by list? operator:
> (list? 1)
#f
> (list? (cons 1 (cons 2 nil)))
#t
> (list? nil)
#tWhether a list is empty can be determined using the primitive null? predicate.
Using it, we can define the standard sequence operations for computing length and selecting elements:
(define (length items)
(if (null? items)
0
(+ 1 (length (cdr items)))))
(define (getitem items n)
(if (= n 0)
(car items)
(getitem (cdr items) (- n 1))))
> (define squares (list 1 4 9 16 25))
> (length squares)
5
> (getitem squares 3)
16Symbolic Data
In Scheme, we refer to the symbols rather than their values by preceding them with a single quotation mark ' (short for (quote name)):
> (define a 1)
> (define b 2)
> (list a b)
(1 2)
> (list 'a 'b)
(a b)
> (list 'a b)
(a 2)
> (list 'define 'list)
(define list)Quotation also allows us to type in compound objects, using the conventional printed representation for lists:
> (car '(a b c))
a
> (cdr '(a b c))
(b c)List Processing
Built-in Procedures
(append s t)List the elements ofsandt- Can be called on more than 2 lists
(map f s)Call a procedurefon each element of a listsand list the results(filter f s)Call a procedurefon each element of a listsand list the elements for which a true value is the result(apply f s)Call a procedurefwith the elements of a list as its arguments
> (apply + '(1 2 3 4))
10Example: Even Subsets
The even subsets of s include:
- All the even subsets of the rest of
s - The first element of
sfollowed by an (even/odd) subset of the rest - Just the first element of
sif it is even
(define (even-subsets s)
(if (null? s)
nil
(append (even-subsets (cdr s))
(map (lambda (t) (cons (car s) t))
(if (even? (car s))
(even-subsets (cdr s))
(odd-subsets (cdr s))
)
)
(if (even? (car s)) (list (list(car s))) nil)
)
)
)
(define (odd-subsets s)
(if (null? s)
nil
(append (odd-subsets (cdr s))
(map (lambda (t) (cons (car s) t))
(if (odd? (car s))
(even-subsets (cdr s))
(odd-subsets (cdr s))
)
)
(if (odd? (car s)) (list (list(car s))) nil)
)
)
)Simplify it with higher order function
(define (even-subsets s)
(if (null? s)
nil
(append (even-subsets (cdr s))
(subset-helper even? s)
)
)
)
(define (odd-subsets s)
(if (null? s)
nil
(append (odd-subsets (cdr s))
(subset-helper odd? s)
)
)
)
(define (subsethelper f s)
(map (lambda (t) (cons (car s) t))
(if (f (car s))
(even-subsets (cdr s))
(odd-subsets (cdr s))
)
)
(if (f (car s)) (list (list(car s))) nil)
)Even simpler approach using filter
(define (nonempty-subsets s)
(if (null? s)
nil
(let ((rest (nonempty-subsets (cdr s))))
(append rest
(map (lambda (t) (cons (car s) t )) rest)
(list (list (car s)))
)
)
)
)
(define (even-subsets s)
(filter
(lambda (s) (even? (apply + s)))
(nonempty-subsets s)
)
)