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 param
s 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 test
s 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 test
s in order, returning the first true value. If no test
is true and there are no more test
s 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)
)