sicp/2-2.org

686 lines
21 KiB
Org Mode

#+BEGIN_HTML
---
title: 2.2 - Hierarchical Data and the Closure Property
layout: org
---
#+END_HTML
* Representing Sequences
** List Operations
*** Exercise 2.17
Define a procedure `last-pair' that returns the
list that contains only the last element of a given (nonempty)
list:
#+begin_src scheme
(last-pair (list 23 72 149 34))
(34)
#+end_src
--------------------------------------------------------------------
#+begin_src scheme :tangle yes
;; -------------------------------------------------------------------
;; Exercise 2.17
;; -------------------------------------------------------------------
(define (last-pair list)
(cond ((null? list) ())
((null? (cdr list)) list)
(else (last-pair (cdr list)))))
#+end_src
*** Exercise 2.18
Define a procedure `reverse' that takes a list as
argument and returns a list of the same elements in reverse order:
#+begin_src scheme
(reverse (list 1 4 9 16 25))
(25 16 9 4 1)
#+end_src
--------------------------------------------------------------------
#+begin_src scheme :tangle yes
;; -------------------------------------------------------------------
;; Exercise 2.18
;; -------------------------------------------------------------------
(define (reverse list)
(define (iter original new)
(if (null? original) new
(let ((head (car original))
(tail (cdr original)))
(iter tail (cons head new)))))
(iter list ()))
#+end_src
*** Exercise 2.19
Consider the change-counting program of section
*Note 1-2-2::. It would be nice to be able to easily change the
currency used by the program, so that we could compute the number
of ways to change a British pound, for example. As the program is
written, the knowledge of the currency is distributed partly into
the procedure `first-denomination' and partly into the procedure
`count-change' (which knows that there are five kinds of U.S.
coins). It would be nicer to be able to supply a list of coins to
be used for making change.
We want to rewrite the procedure `cc' so that its second argument
is a list of the values of the coins to use rather than an integer
specifying which coins to use. We could then have lists that
defined each kind of currency:
#+begin_src scheme :tangle yes
;; -------------------------------------------------------------------
;; Exercise 2.19
;; -------------------------------------------------------------------
(define us-coins (list 50 25 10 5 1))
(define uk-coins (list 100 50 20 10 5 2 1 0.5))
#+end_src
We could then call `cc' as follows:
#+begin_src scheme
(cc 100 us-coins)
292
#+end_src
To do this will require changing the program `cc' somewhat. It
will still have the same form, but it will access its second
argument differently, as follows:
#+begin_src scheme :tangle yes
(define (cc amount coin-values)
(cond ((= amount 0) 1)
((or (< amount 0) (no-more? coin-values)) 0)
(else
(+ (cc amount
(except-first-denomination coin-values))
(cc (- amount
(first-denomination coin-values))
coin-values)))))
#+end_src
Define the procedures `first-denomination',
`except-first-denomination', and `no-more?' in terms of primitive
operations on list structures. Does the order of the list
`coin-values' affect the answer produced by `cc'? Why or why not?
----------------------------------------------------------------------
#+begin_src scheme :tangle yes
(define first-denomination car)
(define except-first-denomination cdr)
(define no-more? null?)
#+end_src
*** Exercise 2.20
The procedures `+', `*', and `list' take
arbitrary numbers of arguments. One way to define such procedures
is to use `define' with notation "dotted-tail notation". In a
procedure definition, a parameter list that has a dot before the
last parameter name indicates that, when the procedure is called,
the initial parameters (if any) will have as values the initial
arguments, as usual, but the final parameter's value will be a "list"
of any remaining arguments. For instance, given the definition
#+begin_src scheme
(define (f x y . z) <BODY>)
#+end_src
the procedure `f' can be called with two or more arguments. If we
evaluate
#+begin_src scheme
(f 1 2 3 4 5 6)
#+end_src
then in the body of `f', `x' will be 1, `y' will be 2, and `z'
will be the list `(3 4 5 6)'. Given the definition
#+begin_src scheme
(define (g . w) <BODY>)
#+end_src
the procedure `g' can be called with zero or more arguments. If we
evaluate
#+begin_src scheme
(g 1 2 3 4 5 6)
#+end_src
then in the body of `g', `w' will be the list `(1 2 3 4 5 6)'.(4)
Use this notation to write a procedure `same-parity' that takes
one or more integers and returns a list of all the arguments that
have the same even-odd parity as the first argument. For example,
#+begin_src scheme
(same-parity 1 2 3 4 5 6 7)
(1 3 5 7)
(same-parity 2 3 4 5 6 7)
(2 4 6)
#+end_src
----------------------------------------------------------------------
#+begin_src scheme :tangle yes
;; -------------------------------------------------------------------
;; Exercise 2.20
;; -------------------------------------------------------------------
(define (same-parity n . rest)
(define (iter predicate original filtered)
(cond ((null? original) filtered)
((predicate (car original))
(iter predicate
(cdr original)
(append filtered (list (car original)))))
(else (iter predicate
(cdr original)
filtered))))
(iter (if (even? n) even? odd?)
(cons n rest)
'()))
#+end_src
** Mapping over lists
*** Exercise 2.21:
The procedure `square-list' takes a list of numbers as argument
and returns a list of the squares of those numbers.
#+begin_src scheme
(square-list (list 1 2 3 4))
(1 4 9 16)
#+end_src
Here are two different definitions of `square-list'. Complete
both of them by filling in the missing expressions:
#+begin_src scheme
(define (square-list items)
(if (null? items)
nil
(cons <??> <??>)))
(define (square-list items)
(map <??> <??>))
#+end_src
----------------------------------------------------------------------
#+begin_src scheme :tangle yes
;; -------------------------------------------------------------------
;; Exercise 2.21
;; -------------------------------------------------------------------
(define (square-list items)
(if (null? items)
'()
(cons (* (car items) (car items)) (square-list (cdr items)))))
(define (square-list items)
(map (lambda (x) (* x x))
items))
#+end_src
*** Exercise 2.22:
Louis Reasoner tries to rewrite the first `square-list' procedure
of *Note Exercise 2-21:: so that it evolves an iterative process:
#+begin_src scheme
(define (square-list items)
(define (iter things answer)
(if (null? things)
answer
(iter (cdr things)
(cons (square (car things))
answer))))
(iter items nil))
#+end_src
Unfortunately, defining `square-list' this way produces the answer
list in the reverse order of the one desired. Why?
Louis then tries to fix his bug by interchanging the arguments to
`cons':
#+begin_src scheme
(define (square-list items)
(define (iter things answer)
(if (null? things)
answer
(iter (cdr things)
(cons answer
(square (car things))))))
(iter items nil))
#+end_src
This doesn't work either. Explain.
----------------------------------------------------------------------
The first iterative rewrite reads the items from first to last,
but builds the list last to first (cons effectively prepends the
answer to the list of results).
The second version attempts to reverse the arguments of cons,
however this doesn't build a proper list. Normally, a list is a
value paired with a list in the second slot. This pairs a list
with a value in the second slot.
*** Exercise 2.23
The procedure `for-each' is similar to `map'. It takes as
arguments a procedure and a list of elements. However, rather
than forming a list of the results, `for-each' just applies the
procedure to each of the elements in turn, from left to right.
The values returned by applying the procedure to the elements are
not used at all--`for-each' is used with procedures that perform
an action, such as printing. For example,
#+begin_src scheme
(for-each (lambda (x) (newline) (display x))
(list 57 321 88))
57
321
88
#+end_src
The value returned by the call to `for-each' (not illustrated
above) can be something arbitrary, such as true. Give an
implementation of `for-each'.
----------------------------------------------------------------------
#+begin_src scheme :tangle yes
;; -------------------------------------------------------------------
;; Exercise 2.23
;; -------------------------------------------------------------------
(define (for-each fun list)
(if (null? list)
#t
))
#+end_src
* Hierarchical Structures
** Exercise 2.24
Suppose we evaluate the expression `(list 1 (list
2 (list 3 4)))'. Give the result printed by the interpreter, the
corresponding box-and-pointer structure, and the interpretation of
this as a tree (as in *Note Figure 2-6::).
-------------------------------------------------------------------
#+begin_src scheme :tangle yes
;; -------------------------------------------------------------------
;; Exercise 2.24
;; -------------------------------------------------------------------
'(1 (2 (3 4)))
;; [ * | * ]
;; ↓ ↓
;; 1 [ * | * ]
;; ↓ ↓
;; 2 [ * | * ] → [ * | / ]
;; ↓ ↓
;; 3 4
;; *
;; / \
;; 1 *
;; / \
;; 2 *
;; / \
;; 3 4
#+end_src
** Exercise 2.25
Give combinations of `car's and `cdr's that will
pick 7 from each of the following lists:
#+begin_src scheme
(1 3 (5 7) 9)
((7))
(1 (2 (3 (4 (5 (6 7))))))
#+end_src
----------------------------------------------------------------------
#+begin_src scheme :tangle yes
;; -------------------------------------------------------------------
;; Exercise 2.25
;; -------------------------------------------------------------------
(car (cdr (car (cdr (cdr '(1 3 (5 7) 9))))))
(car (car '((7))))
(car (cdr (car (cdr (car (cdr (car (cdr (car (cdr (car (cdr '(1 (2 (3 (4 (5 (6 7))))))))))))))))))
#+end_src
** Exercise 2.26
Suppose we define `x' and `y' to be two lists:
#+begin_src scheme
(define x (list 1 2 3))
(define y (list 4 5 6))
#+end_src
What result is printed by the interpreter in response to
evaluating each of the following expressions:
#+begin_src scheme
(append x y)
(cons x y)
(list x y)
#+end_src
----------------------------------------------------------------------
#+begin_src scheme
;; -------------------------------------------------------------------
;; Exercise 2.26
;; -------------------------------------------------------------------
;; (append x y)
'(1 2 3 4 5 6)
;; (cons x y)
'((1 2 3) 4 5 6)
;; (list x y)
'((1 2 3) (4 5 6))
#+end_src
** Exercise 2.27
Modify your `reverse' procedure of *Note Exercise
2-18:: to produce a `deep-reverse' procedure that takes a list as
argument and returns as its value the list with its elements
reversed and with all sublists deep-reversed as well. For example,
#+begin_src scheme
(define x (list (list 1 2) (list 3 4)))
x
((1 2) (3 4))
(reverse x)
((3 4) (1 2))
(deep-reverse x)
((4 3) (2 1))
#+end_src
----------------------------------------------------------------------
#+begin_src scheme :tangle yes
;; -------------------------------------------------------------------
;; Exercise 2.27
;; -------------------------------------------------------------------
(define (deep-reverse list)
(define (iter original new)
(if (null? original) new
(let ((head (car original))
(tail (cdr original)))
(iter tail (cons
(if (pair? head)
(deep-reverse head)
head)
new)))))
(iter list ()))
#+end_src
** Exercise 2.28
Write a procedure `fringe' that takes as argument
a tree (represented as a list) and returns a list whose elements
are all the leaves of the tree arranged in left-to-right order.
For example,
#+begin_src scheme
(define x (list (list 1 2) (list 3 4)))
(fringe x)
(1 2 3 4)
(fringe (list x x))
(1 2 3 4 1 2 3 4)
#+end_src
----------------------------------------------------------------------
#+begin_src scheme
(define (fringe tree)
(define (iter original new)
(if (null? original) new
(let ((head (car original))
(tail (cdr original)))
(if (pair? head)
(iter (append head tail) new)
(iter tail (cons head new))))))
(iter (deep-reverse tree) '()))
#+end_src
** Exercise 2.29
A binary mobile consists of two branches, a left
branch and a right branch. Each branch is a rod of a certain
length, from which hangs either a weight or another binary mobile.
We can represent a binary mobile using compound data by
constructing it from two branches (for example, using `list'):
#+begin_src scheme
(define (make-mobile left right)
(list left right))
#+end_src
A branch is constructed from a `length' (which must be a number)
together with a `structure', which may be either a number
(representing a simple weight) or another mobile:
#+begin_src scheme
(define (make-branch length structure)
(list length structure))
#+end_src
a. Write the corresponding selectors `left-branch' and
`right-branch', which return the branches of a mobile, and
`branch-length' and `branch-structure', which return the
components of a branch.
b. Using your selectors, define a procedure `total-weight' that
returns the total weight of a mobile.
c. A mobile is said to be "balanced" if the torque applied by
its top-left branch is equal to that applied by its top-right
branch (that is, if the length of the left rod multiplied by
the weight hanging from that rod is equal to the
corresponding product for the right side) and if each of the
submobiles hanging off its branches is balanced. Design a
predicate that tests whether a binary mobile is balanced.
d. Suppose we change the representation of mobiles so that the
constructors are
#+begin_src scheme
(define (make-mobile left right)
(cons left right))
(define (make-branch length structure)
(cons length structure))
#+end_src
How much do you need to change your programs to convert to
the new representation?
----------------------------------------------------------------------
#+begin_src scheme :tangle yes
;; -------------------------------------------------------------------
;; Exercise 2.29
;; -------------------------------------------------------------------
(define (make-mobile left right)
(list left right))
(define (make-branch length structure)
(list length structure))
(define left-branch car)
(define right-branch cadr)
(define branch-length car)
(define branch-structure cadr)
;; Test Data
;; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(define (make-test-mobile)
(make-mobile
(make-branch 3 4)
(make-branch 1
(make-mobile
(make-branch 1 10)
(make-branch 5 2)))))
;; Calculations
;; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(define (branch-weight branch)
(let ((structure (branch-structure branch)))
(if (number? structure)
structure
(+ (branch-weight (left-branch structure))
(branch-weight (right-branch structure))))))
(define (total-weight mobile)
(+ (branch-weight (left-branch mobile))
(branch-weight (right-branch mobile))))
(define (torque branch)
(* (branch-length branch)
(branch-weight branch)))
(define (balanced? mobile)
(define (balanced-branch? branch)
(let ((structure (branch-structure branch)))
(if (number? structure)
#t
(balanced? structure))))
(let ((left (left-branch mobile))
(right (right-branch mobile)))
(and
(= (torque left)
(torque right))
(and (balanced-branch? left)
(balanced-branch? right)))))
;; New representation
;; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(define (make-mobile left right)
(cons left right))
(define (make-branch length structure)
(cons length structure))
(define right-branch cdr)
(define branch-structure cdr)
#+end_src
** Mapping over trees
*** Exercise 2.30
Define a procedure `square-tree' analogous to the `square-list'
procedure of *Note Exercise 2-21::. That is, `square-list' should
behave as follows:
#+begin_src scheme
(square-tree
(list 1
(list 2 (list 3 4) 5)
(list 6 7)))
(1 (4 (9 16) 25) (36 49))
#+end_src
Define `square-tree' both directly (i.e., without using any
higher-order procedures) and also by using `map' and recursion.
----------------------------------------------------------------------
#+begin_src scheme :tangle yes
;; -------------------------------------------------------------------
;; Exercise 2.30
;; -------------------------------------------------------------------
(define (square-tree tree)
(cond ((null? tree) '())
((not (pair? tree)) (square tree))
(else (cons (square-tree (car tree))
(square-tree (cdr tree))))))
(define (square-tree tree)
(map (lambda (sub-tree)
(if (pair? sub-tree)
(square-tree sub-tree)
(square sub-tree)))
tree))
#+end_src
*** Exercise 2.31
Abstract your answer to *Note Exercise 2-30:: to produce a
procedure `tree-map' with the property that `square-tree' could be
defined as
#+begin_src scheme
(define (square-tree tree) (tree-map square tree))
#+end_src
----------------------------------------------------------------------
#+begin_src scheme :tangle yes
;; -------------------------------------------------------------------
;; Exercise 2.31
;; -------------------------------------------------------------------
(define (tree-map f tree)
(map (lambda (sub-tree)
(if (pair? sub-tree)
(tree-map f sub-tree)
(f sub-tree)))
tree))
(define (square-tree tree)
(tree-map square tree))
#+end_src
*** Exercise 2.32
We can represent a set as a list of distinct elements, and we can
represent the set of all subsets of the set as a list of lists.
For example, if the set is `(1 2 3)', then the set of all subsets
is `(() (3) (2) (2 3) (1) (1 3) (1 2) (1 2 3))'. Complete the
following definition of a procedure that generates the set of
subsets of a set and give a clear explanation of why it works:
#+begin_src scheme
(define (subsets s)
(if (null? s)
(list nil)
(let ((rest (subsets (cdr s))))
(append rest (map <??> rest)))))
#+end_src
----------------------------------------------------------------------
#+begin_src scheme :tangle yes
(define (subsets s)
(if (null? s)
(list '())
(let ((rest (subsets (cdr s))))
(append rest (map (lambda (x) (cons (car s) x)) rest)))))
#+end_src