The Lisp programming language was introduced in 1958.
The Scheme dialect of Lisp was introduced in the 1970s, and is still maintained by a standards committee today.
Genealogical tree of programming languagesScheme itself is not commonly used in production, but has influenced many other languages, and is a good example of a functional programming language.
Scheme programs consist of expressions, which can be:
2 3.3 #t #f + quotient(quotient 10 2) (not #t)Call expressions include an operator and 0 or more operands in parentheses:
> (quotient 10 2)
5
> (quotient (+ 8 7) 5)
3
> (+ (* 3
(+ (* 2 4)
(+ 3 5)))
(+ (- 10 7)
6))
| Name | Example |
|---|---|
+ |
(+ 1 2 3) |
- |
(- 12) (- 3 2 1) |
* |
(*) (* 2) (* 2 3) |
/ |
(/ 2) (/ 4 2) (/ 16 2 2) |
quotient |
(quotient 7 3) |
abs |
(abs -12) |
expt |
(expt 2 10) |
remainder |
(remainder 7 3) (remainder -7 3) |
modulo |
(modulo 7 3) (modulo -7 3) |
These procedures only work on numbers:
| Name | True expressions |
|---|---|
= |
(= 4 4) (= 4 (+ 2 2)) |
< |
(< 4 5) |
> |
(> 5 4) |
<= |
(<= 4 5) (<= 4 4) |
>= |
(>= 5 4) (>= 4 4) |
even? |
(even? 2) |
odd? |
(odd? 3) |
zero? |
(zero? 0) (zero? 0.0) |
These procedures work on all data types:
| Name | True expressions | False expressions |
|---|---|---|
eq |
(eq? #t #t) (eq? 0 (- 1 1)) |
(eq? #t #f) (eq? 0 0.0) |
not |
(not #f) |
(not 0) (not #t) |
The only falsey value in Scheme is #f.
All other values are truthy.
A combination that is not a call expression is a special form:
(if <predicate> <consequent> <alternative>)
(and <e1> ... <en>) (or <e1> ... <en>)
(define <symbol> <expression>)
(define (<symbol> <formal parameters>) <body>)
define <name> <expression>
Evaluates <expression> and binds the value to <name> in the current
environment. <name> must be a valid Scheme symbol.
(define x 2)
define (<name> [param] ...) <body>)
Constructs a new procedure with params as its parameters and the body
expressions as its body and binds it to name in the current environment.
name must be a valid Scheme symbol. Each param must be a unique valid Scheme
symbol.
(define (double x) (* 2 x) )
if <predicate> <consequent> <alternative>
Evaluates predicate. If true, the consequent is evaluated and returned.
Otherwise, the alternative, if it exists, is evaluated and returned (if no
alternative is present in this case, the return value is undefined).
Example: This code evaluates to 100/x for non-zero numbers and 0 otherwise:
(define x 5)
(if (zero? x)
0
(/ 100 x))
(and [test] ...)
Evaluate the tests in order, returning the first false value. If no test
is false, return the last test. If no arguments are provided, return #t.
Example:
This and form evaluates to true whenever x is both greater than 10 and less than 20.
(define x 15)
(and (> x 10) (< x 20))
(or [test] ...)
Evaluate the tests in order, returning the first true value. If no test
is true and there are no more tests left, return #f.
Example:
This or form evaluates to true when either x is less than -10 or greater than 10.
(define x -15)
(or (< x -10) (> x 10))
The cond special form that behaves similar to if expressions in Python.
if x > 10:
print('big')
elif x > 5:
print('medium')
else:
print('small')
(cond ((> x 10) (print 'big))
((> x 5) (print 'medium))
(else (print 'small)))
(print (cond ((> x 10) 'big)
((> x 5) 'medium)
(else 'small)))
Without cond, we'd have deeply nested if forms:
(if (> x 10) (print 'big)
(if (> x 5) (print 'medium)
(print 'small)
)
)
So much nicer with cond!
(cond
((> x 10) (print 'big))
((> x 5) (print 'medium))
(else (print 'small)))
if x > 10:
print('big')
print('pie')
else:
print('small')
print('fry')
(cond ((> x 10) (begin (print 'big) (print 'pie)))
(else (begin (print 'small) (print 'fry))))
(if (> x 10) (begin
(print 'big)
(print 'pie))
(begin
(print 'small)
(print 'fry)))
The let special form binds symbols to values temporarily; just for one expression
a = 3
b = 2 + 2
c = math.sqrt(a * a + b * b)
⬆️ a and b are still bound down here
(define c (let ((a 3)
(b (+ 2 2)))
(sqrt (+ (* a a) (* b b)))))
⬆️ a and b are not bound down here
Lambda expressions evaluate to anonymous procedures.
(lambda ([param] ...) <body> ...)
Two equivalent expressions:
(define (plus4 x) (+ x 4))
(define plus4 (lambda (x) (+ x 4)))
An operator can be a lambda expression too:
((lambda (x y z) (+ x y (square z))) 1 2 3)
What's the sum of the squares of even numbers less than 10, starting with some number?
Python version (iterative):
def sum_of_squares(num):
total = 0
while num < 10:
total += num ** 2
num += 2
return total
sum_of_squares(2) # 120
Python version (recursive):
def sum_of_squares(num, total):
if num >= 10:
return total
else:
return sum_of_squares(num + 2, total + num ** 2)
sum_of_squares(2, 0) # 120
Scheme version:
(define (sum_of_squares num total)
(if (>= num 10)
total
(sum_of_squares (+ num 2) (+ total (* num num)))
)
)
(sum_of_squares 2 0)
What if we said the sum_of_squares function could only take one argument?
In Python, we could use a helper function:
def sum_of_squares(num):
def with_total(num, total):
if num >= 10:
return total
else:
return with_total(num + 2, total + num ** 2)
return with_total(num, 0)
Similarly in Scheme!
(define (sum_of_squares num)
(define (with_total num total)
(if (>= num 10)
total
(with_total (+ num 2) (+ total (* num num)))
)
)
(with_total num 0)
)