Discussion 9: Scheme Lists, Interpreters
Scheme Lists
As you read through this section, it may be difficult to understand the differences between the various representations of Scheme containers. We recommend that you use our online Scheme interpreter to see the box-and-pointer diagrams of pairs and lists that you're having a hard time visualizing! (Use the command
(autodraw)
to toggle the automatic drawing of diagrams.)
Lists
Scheme lists are very similar to the linked lists we've been working with in
Python. Just like how a linked list is constructed of a series of Link
objects, a Scheme list is constructed with a series of pairs, which are created
with the constructor cons
.
Scheme lists require that the cdr
is either another list or nil
, an empty list.
A list is displayed in the interpreter as a sequence of values (similar to the
__str__
representation of a Link
object). For example,
scm> (cons 1 (cons 2 (cons 3 nil)))
(1 2 3)
Here, we've ensured that the second argument of each cons
expression is
another cons
expression or nil
.
We can retrieve values from our list with the car
and cdr
procedures, which
now work similarly to the Python Link
's first
and rest
attributes.
(Curious about where these weird names come from? Check out their
etymology.)
scm> (define a (cons 1 (cons 2 (cons 3 nil)))) ; Assign the list to the name a
a
scm> a
(1 2 3)
scm> (car a)
1
scm> (cdr a)
(2 3)
scm> (car (cdr (cdr a)))
3
If you do not pass in a pair or nil as the second argument to cons
, it will
error:
scm> (cons 1 2)
Error
list
Procedure
There are a few other ways to create lists. The list
procedure takes in an
arbitrary number of arguments and constructs a list with the values of these
arguments:
scm> (list 1 2 3)
(1 2 3)
scm> (list 1 (list 2 3) 4)
(1 (2 3) 4)
scm> (list (cons 1 (cons 2 nil)) 3 4)
((1 2) 3 4)
Note that all of the operands in this expression are evaluated before being put into the resulting list.
Quote Form
We can also use the quote form to create a list, which will construct the exact
list that is given. Unlike with the list
procedure, the argument to '
is
not evaluated.
scm> '(1 2 3)
(1 2 3)
scm> '(cons 1 2) ; Argument to quote is not evaluated
(cons 1 2)
scm> '(1 (2 3 4))
(1 (2 3 4))
Built-In Procedures for Lists
There are a few other built-in procedures in Scheme that are used for lists. Try them out in the interpreter!
scm> (null? nil) ; Checks if a value is the empty list
True
scm> (append '(1 2 3) '(4 5 6)) ; Concatenates two lists
(1 2 3 4 5 6)
scm> (length '(1 2 3 4 5)) ; Returns the number of elements in a list
5
Q1: Pair Up
Implement pair-up
, which takes a list s
. It returns a list of lists that
together contain all of the elements of s
in order. Each list in the result
should have 2 elements. The last one can have up to 3.
Q2: List Insert
Write a Scheme function that, when given an element, a list, and an index, inserts the element into the list at that index. You can assume that the index is in bounds for the list.
Run in 61A CodeQ3: Interleave
Implement the function interleave
, which takes two lists lst1
and lst2
as
arguments. interleave
should return a list that interleaves the elements
of the two lists. (In other words, the resulting list should contain elements
alternating between lst1
and lst2
, starting at lst1
).
If one of the input lists to interleave
is shorter than the other, then
interleave
should alternate elements from both lists until one list has no
more elements, and then the remaining elements from the longer list should be
added to the end of the new list. If lst1
is empty, you may simply return
lst2
and vice versa.
Scheme Call Expressions
Pair
instance in Python.
For example, the call expression (+ (* 3 4) 5)
is represented as:
Pair('+', Pair(Pair('*', Pair(3, Pair(4, nil))), Pair(5, nil)))
The Pair
class and nil
object are defined in pair.py of the Scheme project.
class Pair:
"A Scheme list is a Pair in which rest is a Pair or nil."
def __init__(self, first, rest):
self.first = first
self.rest = rest
... # There are also __str__, __repr__, and map methods, omitted here.
Q4: Representing Expressions
Write the Scheme expression in Scheme syntax represented by each Pair
below.
Try drawing the linked list diagram too.
>>> Pair('+', Pair(Pair('*', Pair(3, Pair(4, nil))), Pair(5, nil)))
>>> Pair('+', Pair(1, Pair(Pair('*', Pair(2, Pair(3, nil))), nil)))
>>> Pair('and', Pair(Pair('<', Pair(1, Pair(0, nil))), Pair(Pair('/', Pair(1, Pair(0, nil))), nil)))
Evaluation
(+ (* 3 4) 5)
using the interpreter,
scheme_eval
is called on the following expressions (in this order):
(+ (* 3 4) 5)
+
(* 3 4)
*
3
4
5
The *
is evaluated because it is the operator sub-expression of (* 3 4)
,
which is an operand sub-expression of (+ (* 3 4) 5)
.
By default, *
evaluates to a procedure that multiplies its arguments together.
But *
could be redefined at any time, and so the symbol *
must be evaluated
each time it is used in order to look up its current value.
scm> (* 2 3) ; Now it multiplies
6
scm> (define * +)
*
scm> (* 2 3) ; Now it adds
5
Q5: Evaluation
Which of the following are evaluated when scheme_eval
is called on
(if (< x 0) (- x) (if (= x -2) 100 y))
in an environment in which x
is bound to -2?
(Assume <
, -
, and =
have their default values.)
if
<
=
x
y
0
-2
100
-
(
)
Q6: Print Evaluated Expressions
Define print_evals
, which takes a Scheme expression expr
that contains only
numbers, +
, *
, and parentheses. It prints all of the expressions that are
evaluated during the evaluation of expr
. They are printed in the order that
they are passed to scheme_eval
.
Note: Calling print
on a Pair
instance will print the Scheme expression it represents.
>>> print(Pair('+', Pair(Pair('*', Pair(3, Pair(4, nil))), Pair(5, nil))))
(+ (* 3 4) 5)
Run in 61A Code
Challenge
Q7: Slice It!
Implement the get-slicer
procedure, which takes integers a
and b
and returns an a-b slicing function. An a-b
slicing function takes in a list as input and outputs a new list with the values of the original list from index a
(inclusive) to index b
(exclusive).
Your implementation should behave like Python slicing, but should assume a step size of one with no negative slicing indices. Indices start at zero.
Note: the skeleton code is just a suggestion. Feel free to use your own structure if you prefer.
Run in 61A CodeSubmit Attendance
You're done! Excellent work this week. Please be sure to ask your section TA for the attendance form link and fill it out for credit. (one submission per person per section).