about summary refs log tree commit diff stats
path: root/js/games/nluqo.github.io/~bh/ssch25/spread-implement
diff options
context:
space:
mode:
authorelioat <elioat@tilde.institute>2023-08-23 07:52:19 -0400
committerelioat <elioat@tilde.institute>2023-08-23 07:52:19 -0400
commit562a9a52d599d9a05f871404050968a5fd282640 (patch)
tree7d3305c1252c043bfe246ccc7deff0056aa6b5ab /js/games/nluqo.github.io/~bh/ssch25/spread-implement
parent5d012c6c011a9dedf7d0a098e456206244eb5a0f (diff)
downloadtour-562a9a52d599d9a05f871404050968a5fd282640.tar.gz
*
Diffstat (limited to 'js/games/nluqo.github.io/~bh/ssch25/spread-implement')
-rw-r--r--js/games/nluqo.github.io/~bh/ssch25/spread-implement1835
1 files changed, 1835 insertions, 0 deletions
diff --git a/js/games/nluqo.github.io/~bh/ssch25/spread-implement b/js/games/nluqo.github.io/~bh/ssch25/spread-implement
new file mode 100644
index 0000000..dfc7d41
--- /dev/null
+++ b/js/games/nluqo.github.io/~bh/ssch25/spread-implement
@@ -0,0 +1,1835 @@
+<P>
+
+<P><A NAME="accountant"></A><CENTER><IMG SRC="../ss-pics/accountant.jpg" ALT="figure: accountant"></CENTER><P><CENTER>They did spreadsheets by hand in the old
+days.
+</CENTER><P>
+
+<HTML>
+<HEAD>
+<TITLE>Simply Scheme: Introducing Computer Science ch 25: Implementing the Spreadsheet Program</TITLE>
+</HEAD>
+<BODY>
+<HR>
+<CITE>Simply Scheme:</CITE>
+<CITE>Introducing Computer Science</CITE> 2/e Copyright (C) 1999 MIT
+<H2>Chapter 25</H2>
+<H1>Implementing the Spreadsheet Program</H1>
+
+<TABLE width="100%"><TR><TD>
+<IMG SRC="../simply.jpg" ALT="cover photo">
+<TD><TABLE>
+<TR><TD align="right"><CITE><A HREF="http://www.cs.berkeley.edu/~bh/">Brian
+Harvey</A><BR>University of California, Berkeley</CITE>
+<TR><TD align="right"><CITE><A HREF="http://ccrma.stanford.edu/~matt">Matthew
+Wright</A><BR>University of California, Santa Barbara</CITE>
+<TR><TD align="right"><BR>
+<TR><TD align="right"><A HREF="../pdf/ssch25.pdf">Download PDF version</A>
+<TR><TD align="right"><A HREF="../ss-toc2.html">Back to Table of Contents</A>
+<TR><TD align="right"><A HREF="../ssch24/spread.html"><STRONG>BACK</STRONG></A>
+chapter thread <A HREF="database.html"><STRONG>NEXT</STRONG></A>
+<TR><TD align="right"><A HREF="http://mitpress.mit.edu/0262082810">MIT
+Press web page for <CITE>Simply Scheme</CITE></A>
+</TABLE></TABLE>
+
+<HR>
+
+<P>This is a big program and you can't keep it all in your head at once.  In
+this chapter, we'll discuss different parts of the program separately.  For
+example, while we're talking about the screen display procedures, we'll just
+take the rest of the program for granted.  We'll assume that we can ask for
+the value in a cell, and some other part of the program will ensure that we
+can find it out.
+
+<P>Our division of the program includes these parts:
+
+<P><P><TABLE><TR><TH align="right" valign="top">&bull;<TD>&nbsp;&nbsp;&nbsp;&nbsp;<TD valign="top">The command processor, which reads user commands and oversees their
+execution.
+</TABLE><TABLE><TR><TH align="right" valign="top">&bull;<TD>&nbsp;&nbsp;&nbsp;&nbsp;<TD valign="top">The specific commands: cell selection commands, <CODE>load</CODE>, and <CODE>put</CODE>.
+</TABLE><TABLE><TR><TH align="right" valign="top">&bull;<TD>&nbsp;&nbsp;&nbsp;&nbsp;<TD valign="top">The formula translator, which turns a formula into an <EM>expression</EM>
+by translating relative cell positions to specific cells.
+</TABLE><TABLE><TR><TH align="right" valign="top">&bull;<TD>&nbsp;&nbsp;&nbsp;&nbsp;<TD valign="top">The dependency manager, which keeps track of which cells' expressions
+depend on which other cells.
+</TABLE><TABLE><TR><TH align="right" valign="top">&bull;<TD>&nbsp;&nbsp;&nbsp;&nbsp;<TD valign="top">The expression evaluator.
+</TABLE><TABLE><TR><TH align="right" valign="top">&bull;<TD>&nbsp;&nbsp;&nbsp;&nbsp;<TD valign="top">The screen printer.
+</TABLE><TABLE><TR><TH align="right" valign="top">&bull;<TD>&nbsp;&nbsp;&nbsp;&nbsp;<TD valign="top">The cell management procedures, which store and retrieve cell
+information for the rest of the program.
+
+</TABLE>
+<P>
+
+<P>The diagram below shows the interconnections among these seven
+parts of the program by showing what information they have to provide for
+each other.
+
+<P><CENTER><IMG SRC="../ss-pics/dataflow.jpg" ALT="figure: dataflow"></CENTER>
+
+<P>(The arrows that <EM>aren't</EM> in the diagram convey as much
+information as the ones that are.  For example, since there is no arrow from
+the evaluator to the printer, we know that when the spreadsheet program
+redisplays the screen, the values printed are found directly in the data
+structure; no new computation of formulas is needed.)
+
+<P><H2>Cells, Cell Names, and Cell IDs</H2>
+
+<P>The spreadsheet program does its work by manipulating cells.  In this
+section we introduce three abstract data types having to do with cells.
+Before we jump into the spreadsheet procedures themselves, we must introduce
+these three types, which are used throughout the program.
+
+<P>Each cell is a data structure that contains various pieces of information,
+including its value and other things that we'll talk about later.  Just as
+these cells are arranged in a two-dimensional pattern on the screen, they
+are kept within the program in a two-dimensional data structure: a vector
+of vectors.
+
+<P>The elements of a vector are selected by number, using <CODE>vector-ref</CODE>.
+Therefore, if we're looking for a particular cell, such as <CODE>c5</CODE>, what
+the program really wants to know is that this cell is in column 3, row
+5.<A NAME="text1" HREF="spread-implement#ft1">[1]</A> If the program refers to cells by
+name, then there will be several occasions for it to split the word <CODE>c5</CODE>
+into its pieces <CODE>c</CODE> and <CODE>5</CODE>, and to convert the letter <CODE>c</CODE> into
+the number 3.  These operations are fairly slow.  To avoid carrying them out
+repeatedly, the spreadsheet program identifies cells internally using a form
+that's invisible to the person using the program, called a &quot;cell ID.&quot;
+
+<P><A NAME="g1"></A>
+<A NAME="g2"></A>
+Therefore, there are three different <A NAME="g3"></A><A NAME="g4"></A>abstract data types in the
+program that have to do with cells: cell names, such as <CODE>c5</CODE>; cell IDs;
+and cells themselves.  We've chosen to represent cell IDs as three-element
+lists like this one:
+
+<P><PRE>(id 3 5)
+</PRE>
+
+
+<P>but you won't see much evidence of that fact within the program
+because we've implemented selectors and constructors for all of these three
+types.  The representation includes the word <CODE>id</CODE> because one facility
+that the program needs is the ability to determine whether some datum is or
+is not a cell ID.  The predicate <CODE>cell-id?</CODE> looks for a list whose first
+element is <CODE>id</CODE>.
+
+<P>The selectors for cell IDs are <CODE>id-row</CODE> and <CODE>id-col</CODE>; both take a
+cell ID as argument and return a number.  The constructor, <CODE>make-id</CODE>,
+takes a column number (not a letter) and a row number as arguments.
+
+<P>When the program recognizes a cell name typed by the user, it calls <CODE>cell-name-&gt;id</CODE> to translate the name to an ID, and only the ID is stored for
+later use.
+
+<P>(These application-specific ADTs are similar to the database of known values
+in the pattern matcher, as opposed to more general-purpose ADTs like
+trees and sentences.)
+
+<P><H2>The Command Processor</H2>
+
+<P>Here's the core of the command processor:
+
+<P><PRE>(define (<A NAME="g5"></A>command-loop)
+  (print-screen)
+  (let ((command-or-formula (read)))
+    (if (equal? command-or-formula 'exit)
+	&quot;Bye!"
+	(begin (process-command command-or-formula)
+	       (command-loop)))))
+</PRE>
+
+<P>This short program runs until the user types <CODE>exit</CODE>, because
+it invokes itself as its last step.  During each invocation, it prints the
+current spreadsheet display, uses <CODE>read</CODE> to read a command, and
+carries out whatever actions that command requires.  Those actions probably
+change something in the spreadsheet data, so the next cycle has to redisplay
+the modified data before accepting another command.
+
+<P><CODE>Print-screen</CODE> is a large chunk of the program and will be discussed
+in its own section.
+
+<P>How does <CODE>process-command</CODE> work?  It looks for the command name (a word
+such as <CODE>put</CODE>) in its list of known commands.  The commands are kept in an
+association list, like this:
+
+<P><PRE>((p &hellip;) (n &hellip;) (b &hellip;) (f &hellip;) (select &hellip;) (put &hellip;) (load &hellip;))
+</PRE>
+
+<P>Each of these sublists contains two elements: the name and the
+procedure that carries out the command.  We'll see shortly how these
+procedures are invoked.
+
+<P>Looking for the command name is a little tricky because in the spreadsheet
+language a command can be invoked either by typing its name inside
+parentheses with arguments, as in Scheme, or by typing the name alone,
+without parentheses, which Scheme wouldn't interpret as a request to invoke
+a procedure.  For example, the argument to <CODE>process-command</CODE> might be a
+list, such as <CODE>(f 3)</CODE>, or just a word, such as <CODE>f</CODE>.  A third case is
+that the argument might not be one of these commands at all, but instead
+might be a formula, just like one that could be used to determine the value
+in a cell.  <CODE>Process-command</CODE> must recognize these three cases:
+
+<P><PRE>(define (<A NAME="g6"></A>process-command command-or-formula)
+  (cond ((and (list? command-or-formula)
+	      (command? (car command-or-formula)))
+	 (execute-command command-or-formula))
+	((command? command-or-formula)
+	 (execute-command (list command-or-formula 1)))
+	(else (exhibit (ss-eval (pin-down command-or-formula
+					  (selection-cell-id)))))))
+</PRE>
+
+<P>The <CODE>command?</CODE> predicate tells whether its argument is one of
+the command names in the list.  As you can see, if a command name is used
+without parentheses, <CODE>process-command</CODE> pretends that it was given an
+argument of <CODE>1</CODE>.
+
+<P><CODE>Execute-command</CODE> looks up the command name in the list of commands,
+then applies the associated procedure to the arguments, if any:
+
+<P>
+<PRE>(define (<A NAME="g7"></A>execute-command command)
+  (apply (get-command (car command))
+	 (cdr command)))
+</PRE>
+
+<P>The <CODE>else</CODE> clause in <CODE>process-command</CODE>, which handles the case
+of a formula typed instead of a command, invokes several procedures
+that you haven't seen yet.  We'll explain them when we get to the section of
+the program that manipulates formulas.  The only one that's used just for
+command processing is <CODE>exhibit</CODE>:
+
+<P><PRE>(define (<A NAME="g8"></A>exhibit val)
+  (show val)
+  (show &quot;Type RETURN to redraw screen&quot;)
+  (read-line)
+  (read-line))
+</PRE>
+
+<P>This prints a value on the screen, gives the user a chance to read
+it, and then, when the user is ready, returns to processing commands.  (This
+will entail redrawing the spreadsheet display; that's why we have to wait for
+the user to hit return.)  The reason that we invoke <CODE>read-line</CODE> twice is
+that the call to <CODE>read</CODE> from <CODE>command-loop</CODE> reads the spreadsheet
+formula you typed but doesn't advance to the next line.  Therefore, the
+first <CODE>read-line</CODE> invocation gobbles that empty line; the second call to
+<CODE>read-line</CODE> reads the (probably empty) line that the user typed in
+response to the prompting message.<A NAME="text2" HREF="spread-implement#ft2">[2]</A>
+
+<P><H2>Cell Selection Commands</H2>
+
+<P>Several commands have to do with selecting a cell.  We'll show just one
+typical procedure, the one that carries out the <CODE>p</CODE> (previous row)
+command:
+
+<P><PRE>(define (<A NAME="g9"></A>prev-row delta)
+  (let ((row (id-row (selection-cell-id))))
+    (if (&lt; (- row delta) 1)
+	(error &quot;Already at top.&quot;)
+	(set-selected-row! (- row delta)))))
+
+(define (<A NAME="g10"></A>set-selected-row! new-row)
+  (select-id! (make-id (id-column (selection-cell-id)) new-row)))
+
+(define (<A NAME="g11"></A>select-id! id)
+  (set-selection-cell-id! id)
+  (adjust-screen-boundaries))
+</PRE>
+ 
+<CODE>Prev-row</CODE> must ensure that the selected cell is within the
+legal boundaries.  Since <CODE>prev-row</CODE> only moves upward, it has to check
+only that we don't go beyond row 1.  (<CODE>Next-row</CODE> will instead check that
+we don't go beyond row 30 in the other direction.) 
+
+<P><CODE>Adjust-screen-boundaries</CODE> checks for the situation in which the newly
+selected cell, although within the bounds of the spreadsheet, is not within
+the portion currently visible on the screen.  In that case the visible
+portion is shifted to include the selected cell.  (The procedure is
+straightforward and uninteresting, so we're not showing it here.  You can
+see it in the complete listing of the spreadsheet program at the end of this
+chapter.)
+
+<P><CODE>Selection-cell-id</CODE> is a procedure that returns the cell ID of the
+cell that's currently selected.  Similarly, <CODE>set-selection-cell-id!</CODE> sets the
+current selection.  There are comparable procedures <CODE>screen-corner-cell-id</CODE> and <CODE>set-screen-corner-cell-id!</CODE> to keep track of
+which cell should be in the upper left corner of the screen display.  There
+is a vector named <CODE>*special-cells*</CODE> that holds these two cell IDs; you
+can see the details in the complete program listing.
+
+<P>
+<H2>The <CODE><B>Load</B></CODE> Command</H2>
+
+<P>Loading commands from files is easy.  The <CODE>command-loop</CODE> procedure,
+which carries out commands from the keyboard, repeatedly reads a command
+with <CODE>read</CODE> and invokes <CODE>process-command</CODE> to carry it out.  To load
+commands from a file, we want to do exactly the same thing, except that we
+read from a file instead of from the keyboard:
+
+<P><PRE>(define (spreadsheet-load filename)
+  (let ((port (open-input-file filename)))
+    (sl-helper port)
+    (close-input-port port)))
+
+(define (sl-helper port)
+  (let ((command (read port)))
+    (if (eof-object? command)
+	'done
+	(begin (show command)
+	       (process-command command)
+	       (sl-helper port)))))
+</PRE>
+
+<P><H2>The <CODE><B>Put</B></CODE> Command</H2>
+
+<P>The <CODE>put</CODE> command takes two arguments, a formula and a place to put it.
+The second of these can specify either a single cell or an entire row or
+column.  (If there is no second argument, then a single cell, namely the
+selected cell, is implied.)  <CODE>Put</CODE> invokes <CODE>put-formula-in-cell</CODE>
+either once or several times, as needed.  If only a single cell is involved,
+then <CODE>put</CODE> calls <CODE>put-formula-in-cell</CODE> directly.  If a row or column
+is specified, then <CODE>put</CODE> uses the auxiliary procedure <CODE>put-all-cells-in-row</CODE> or <CODE>put-all-cells-in-col</CODE> as an intermediary.
+
+<P><PRE>(define (<A NAME="g12"></A>put formula . where)
+  (cond ((null? where)
+	 (put-formula-in-cell formula (selection-cell-id)))
+	((cell-name? (car where))
+	 (put-formula-in-cell formula (cell-name-&gt;id (car where))))
+	((number? (car where))
+	 (put-all-cells-in-row formula (car where)))
+	((letter? (car where))
+	 (put-all-cells-in-col formula (letter-&gt;number (car where))))
+	(else (error &quot;Put it where?&quot;))))
+</PRE>
+
+<P>The only tricky part of this is the first line.  <CODE>Put</CODE> can be
+invoked with either one or two arguments.  Therefore, the dot notation is
+used to allow a variable number of arguments; the parameter <CODE>where</CODE> will
+have as its value not the second argument itself, but a list that either
+contains the second argument or is empty.  Thus, if there is a second
+argument, <CODE>put</CODE> refers to it as <CODE>(car where)</CODE>.
+
+<P><PRE>(define (<A NAME="g13"></A>put-all-cells-in-row formula row)
+  (put-all-helper formula (lambda (col) (make-id col row)) 1 26))
+
+(define (<A NAME="g14"></A>put-all-cells-in-col formula col)
+  (put-all-helper formula (lambda (row) (make-id col row)) 1 30))
+
+(define (<A NAME="g15"></A>put-all-helper formula id-maker this max)
+  (if (&gt; this max)
+      'done
+      (begin (try-putting formula (id-maker this))
+	     (put-all-helper formula id-maker (+ 1 this) max))))
+
+(define (<A NAME="g16"></A>try-putting formula id)
+  (if (or (null? (cell-value id)) (null? formula))
+      (put-formula-in-cell formula id)
+      'do-nothing))
+</PRE>
+
+<P><CODE>Put-all-cells-in-row</CODE> and <CODE>put-all-cells-in-col</CODE> invoke
+<CODE>put-all-helper</CODE>, which repeatedly puts the formula into a
+cell.<A NAME="text3" HREF="spread-implement#ft3">[3]</A> <CODE>Put-all-helper</CODE> is a typical sequential recursion:  Do
+something for this element, and recur for the remaining elements.  The
+difference is that &quot;this element&quot; means a cell ID that combines one
+constant index with one index that varies with each recursive call.  How are
+those two indices combined?  What differs between filling a row and filling
+a column is the <EM>function</EM> used to compute each cell ID.
+
+<P>The substitution model explains how the <CODE>lambda</CODE> expressions used as
+arguments to <CODE>put-all-helper</CODE> implement this idea.  Suppose we are
+putting a formula into every cell in row 4.  Then <CODE>put-all-cells-in-row</CODE>
+will be invoked with the value <CODE>4</CODE> substituted for the parameter <CODE>row</CODE>.  After this substitution, the body is
+
+<P><PRE>(put-all-helper formula (lambda (col) (make-id col 4)) 1 26)
+</PRE>
+
+<P>The <CODE>lambda</CODE> expression creates a procedure that takes a
+column number as argument and returns a cell ID for the cell in row 4 and
+that column.  This is just what <CODE>put-all-helper</CODE> needs.  It invokes the
+procedure with the varying column number as its argument to get the proper
+cell ID.
+
+<P><CODE>Put-all-helper</CODE> doesn't directly invoke <CODE>put-formula-in-cell</CODE>.  The
+reason is that if a particular cell already has a value, then the new
+formula isn't used for that particular cell, unless the formula is empty.
+That is, you can erase an entire row or column at once, but a non-empty
+formula affects only cells that were empty before this command.  <CODE>Try-putting</CODE> decides whether or not to put the formula into each possible
+cell.  (In <CODE>try-putting</CODE>, the third argument to <CODE>if</CODE> could be
+anything; we're interested only in what happens if the condition is true.)
+
+<P>All that's left is, finally, to put the formula into a cell:
+
+<P><PRE>(define (<A NAME="g17"></A>put-formula-in-cell formula id)
+  (put-expr (pin-down formula id) id))
+</PRE>
+
+<P>The two new procedures seen here, <CODE>pin-down</CODE> and <CODE>put-expr</CODE>, are both large sections of the program and are described in the
+next two sections of this chapter.
+
+<P><H2>The Formula Translator</H2>
+
+<P>Suppose the user has said
+
+<P><PRE>(put (* (cell b) (cell c)) d)
+</PRE>
+
+<P>The <CODE>put</CODE> procedure puts this formula into each cell of column
+<CODE>d</CODE> by repeatedly calling <CODE>put-formula-in-cell</CODE>; as an example,
+let's concentrate on cell <CODE>d4</CODE>.
+
+<P>The purpose of the formula is that later we're going to use it to compute a
+value for <CODE>d4</CODE>.  For that purpose, we will need to multiply two
+particular numbers together: the ones in cells <CODE>b4</CODE> and <CODE>c4</CODE>.
+Although the same formula applies to cell <CODE>d5</CODE>, the particular numbers
+multiplied together will be found in different places.  So instead of
+storing the same general formula in every <CODE>d</CODE> cell, what we'd really
+like to store in <CODE>d4</CODE> is something that refers specifically to <CODE>b4</CODE> and <CODE>c4</CODE>.
+
+<P>We'll use the term &quot;expression&quot; for a formula whose cell references have
+been replaced by specific cell IDs.  We started with the idea that we wanted
+to put a formula into a cell; we've refined that idea so that we now want to
+put an expression into the cell.  This goal has two parts:  We must
+translate the formula (as given to us by the user in the <CODE>put</CODE> command)
+to an expression, and we must store that expression in the cell data
+structure.  These two subtasks are handled by <CODE>pin-down</CODE>, discussed in
+this section, and by <CODE>put-expr</CODE>, discussed in the next section.  <CODE>Pin-down</CODE> is entirely functional; the only modification to the spreadsheet
+program's state in this process is carried out by <CODE>put-expr</CODE>.
+
+<P>We'll refer to the process of translating a formula to an expression as
+&quot;pinning down&quot; the formula; the procedure <CODE>pin-down</CODE> carries out this
+process.  It's called &quot;pinning down&quot; because we start with a <EM>general</EM> formula and end up with a <EM>specific</EM> expression.  <CODE>Pin-down</CODE> takes two arguments: The first is, of course, a formula; the
+second is the ID of the cell that will be used as the reference point for
+relative cell positions.  In the context of the <CODE>put</CODE> command, the
+reference point is the cell into which we'll put the expression.  But <CODE>pin-down</CODE> doesn't think about putting anything anywhere; its job is to
+translate a formula (containing relative cell locations) into an expression
+(containing only absolute cell IDs).<A NAME="text4" HREF="spread-implement#ft4">[4]</A> <CODE>Pin-down</CODE> needs a reference point
+as a way to understand the notation <CODE>&lt;3</CODE>, which means &quot;three
+cells before the reference point.&quot;
+
+<P>Let's go back to the specific example.  <CODE>Put-formula-in-cell</CODE> will invoke
+
+<P><PRE>(pin-down '(* (cell b) (cell c)) 'd4)
+</PRE>
+
+<P>and <CODE>pin-down</CODE> will return the expression
+
+<P><PRE>(* (id 2 4) (id 3 4))
+</PRE>
+
+<P>The overall structure of this problem is tree recursive.  That's because a
+formula can be arbitrarily complicated, with sublists of sublists, just like
+a Scheme expression:
+
+<P><PRE>(put (+ (* (cell b) (cell c)) (- (cell 2&lt; 3&gt;) 6)) f)
+</PRE>
+
+<P>Here's <CODE>pin-down</CODE>:
+
+<P><PRE>(define (<A NAME="g18"></A>pin-down formula id)
+  (cond ((cell-name? formula) (cell-name-&gt;id formula))
+	((word? formula) formula)
+	((null? formula) '())
+	((equal? (car formula) 'cell)
+	 (pin-down-cell (cdr formula) id))
+	(else (bound-check
+	       (map (lambda (subformula) (pin-down subformula id))
+		    formula)))))
+</PRE>
+
+
+<P>The base cases of the tree recursion are specific cell names, such
+as <CODE>c3</CODE>; other words, such as numbers and procedure names, which are
+unaffected by <CODE>pin-down</CODE>; null formulas, which indicate that the user is
+erasing the contents of a cell; and sublists that start with the word <CODE>cell</CODE>.  The first three of these are trivial; the fourth, which we will
+describe shortly, is the important case.  If a formula is not one of these
+four base cases, then it's a compound expression.  In that case, we have to
+pin down all of the subexpressions individually.  (We basically <CODE>map</CODE>
+<CODE>pin-down</CODE> over the formula.  That's what makes this process tree
+recursive.)
+
+<P>One complication is that the pinned-down formula might refer to nonexistent
+cells.  For example, saying
+
+<P><PRE>(put (+ (cell 2&lt; 3&lt;) 1) d)
+</PRE>
+
+<P>refers to cells in column <CODE>b</CODE> (two to the left of <CODE>d</CODE>)
+three rows above the current row.  That works for a cell such as <CODE>d7</CODE>,
+referring to cell <CODE>b4</CODE>, but not for <CODE>d2</CODE>, which has no row that's
+three above it.  (There is no row <CODE>-1</CODE>.)   So our program must refrain
+from pinning down this formula for cells <CODE>d1</CODE>, <CODE>d2</CODE>, and <CODE>d3</CODE>.
+<CODE>Pin-down</CODE> will instead return the word <CODE>out-of-bounds</CODE> to signal
+this situation.
+
+<P>The case of a nonexistent cell is discovered by <CODE>pin-down-cell</CODE> at the
+base of a tree recursion.  The <CODE>out-of-bounds</CODE> signal must be returned
+not only by the base case but by the initial invocation of <CODE>pin-down</CODE>.
+That's why <CODE>bound-check</CODE> is used to ensure that if any part of a formula
+is out of bounds, the entire formula will also be considered out of bounds:
+
+<P><PRE>(define (<A NAME="g19"></A>bound-check form)
+  (if (member 'out-of-bounds form)
+      'out-of-bounds
+      form))
+</PRE>
+
+<P>When a formula contains a <CODE>(cell &hellip;)</CODE> sublist, the procedure <CODE>pin-down-cell</CODE> is invoked to translate that notation into a cell ID.
+
+<P>The arguments to <CODE>pin-down-cell</CODE> are a list of the &quot;arguments&quot; of the
+<CODE>cell</CODE> sublist and the reference point's cell ID.  (The word
+&quot;arguments&quot; is in quotation marks because the word <CODE>cell</CODE> doesn't
+represent a Scheme procedure, even though the parenthesized notation looks
+like an invocation.  In a way, the special treatment of <CODE>cell</CODE> by <CODE>pin-down</CODE> is analogous to the treatment of special forms, such as <CODE>cond</CODE>,
+by the Scheme evaluator.)
+
+<P>There can be one or two of these &quot;arguments&quot; to <CODE>cell</CODE>.  A single
+argument is either a number, indicating a row, or a letter, indicating a
+column.  Two arguments specify both a column and a row, in that order:
+
+<P><PRE>(define (<A NAME="g20"></A>pin-down-cell args reference-id)
+  (cond ((null? args)
+	 (error &quot;Bad cell specification: (cell)&quot;))
+	((null? (cdr args))
+	 (cond ((number? (car args))         ; they chose a row
+		(make-id (id-column reference-id) (car args)))
+	       ((letter? (car args))         ; they chose a column
+		(make-id (letter-&gt;number (car args))
+			 (id-row reference-id)))
+	       (else (error &quot;Bad cell specification:"
+			    (cons 'cell args)))))
+	(else
+	 (let ((col (pin-down-col (car args) (id-column reference-id)))
+	       (row (pin-down-row (cadr args) (id-row reference-id))))
+	   (if (and (&gt;= col 1) (&lt;= col 26) (&gt;= row 1) (&lt;= row 30))
+	       (make-id col row)
+	       'out-of-bounds)))))
+</PRE>
+
+<P>In the two-argument case, the job of <CODE>pin-down-col</CODE> and <CODE>pin-down-row</CODE> is to understand notations like <CODE>&lt;3</CODE> for relative
+rows and columns:
+
+<P><PRE>(define (pin-down-col new old)
+  (cond ((equal? new '*) old)
+	((equal? (first new) '&gt;) (+ old (bf new)))
+	((equal? (first new) '&lt;) (- old (bf new)))
+	((letter? new) (letter-&gt;number new))
+	(else (error &quot;What column?&quot;))))
+
+(define (pin-down-row new old)
+  (cond ((number? new) new)
+	((equal? new '*) old)
+	((equal? (first new) '&gt;) (+ old (bf new)))
+	((equal? (first new) '&lt;) (- old (bf new)))
+	(else (error &quot;What row?&quot;))))
+</PRE>
+
+<P><H2>The Dependency Manager</H2>
+
+<P>The remaining part of the <CODE>put</CODE> command is <CODE>put-expr</CODE>, which actually
+stores the translated expression in the cell data structure.  You might
+imagine that putting an expression into a cell would require nothing more
+than invoking a mutator, like this:
+
+<P><PRE>(define (put-expr expr cell)                 ;; wrong
+  (set-cell-expr! cell expr))
+</PRE>
+
+<P>The trouble is that adding an expression to a cell might have many
+consequences beyond the mutation itself.  For example, suppose we say
+
+<P><PRE>(put (+ a3 b2) c4)
+</PRE>
+
+<P>If cells <CODE>a3</CODE> and <CODE>b2</CODE> already have values, we can't just
+put the formula into <CODE>c4</CODE>; we must also compute its value and put that
+value into <CODE>c4</CODE>.
+
+<P>Also, once <CODE>c4</CODE> has a value, that could trigger the computation of
+some other cell's value.  If we've previously said
+
+<P><PRE>(put (+ a3 c4) b5)
+</PRE>
+
+<P>then we're now able to compute a value for <CODE>b5</CODE> because both
+of the cells that it depends on have values.
+
+<P>All this implies that what's inside a cell is more than just an expression, or
+even an expression and a value.  Each cell needs to know which other cells it
+depends on for its value, and also which other cells depend on it.
+
+<P><A NAME="g21"></A>
+<A NAME="g22"></A>
+Our program represents each cell as a four-element vector.  Each cell
+includes a value, an expression, a list of <EM>parents</EM> (the cells that it
+depends on), and a list of <EM>children</EM> (the cells that depend on it).
+The latter two lists contain cell IDs.  So our example cell <CODE>c4</CODE> might
+look like this:
+
+<P><PRE>#(12
+  (+ (id 1 3) (id 2 2))
+  ((id 1 3) (id 2 2))
+  ((id 2 5)))
+</PRE>
+
+<P>In a simpler case, suppose we put a value into a cell that nothing
+depends on, by saying, for example,
+
+<P><PRE>(put 6 a1)
+</PRE>
+
+<P>Then cell <CODE>a1</CODE> would contain
+
+<P><PRE>#(6 6 () ())
+</PRE>
+
+<P>(Remember that a value is just a very simple formula.)
+
+<P>There are selectors <CODE>cell-value</CODE> and so on that take a cell ID as
+argument, and mutators <CODE>set-cell-value!</CODE> and so on that take a cell ID
+and a new value as arguments.  There's also the constructor <CODE>make-cell</CODE>,
+but it's called only at the beginning of the program, when the 780 cells in
+the spreadsheet are created at once.
+
+<P>When a cell is given a new expression, several things change:
+
+<P><P><TABLE><TR><TH align="right" valign="top">&bull;<TD>&nbsp;&nbsp;&nbsp;&nbsp;<TD valign="top">The new expression becomes the cell's expression, of course.
+</TABLE><TABLE><TR><TH align="right" valign="top">&bull;<TD>&nbsp;&nbsp;&nbsp;&nbsp;<TD valign="top">The cells mentioned in the expression become the parents of this cell.
+</TABLE><TABLE><TR><TH align="right" valign="top">&bull;<TD>&nbsp;&nbsp;&nbsp;&nbsp;<TD valign="top">This cell becomes a child of the cells mentioned in the expression.
+</TABLE><TABLE><TR><TH align="right" valign="top">&bull;<TD>&nbsp;&nbsp;&nbsp;&nbsp;<TD valign="top">If all the parents have values, the value of this cell is computed.
+
+</TABLE><P>
+Some of these changes are simple, but others are complicated.  For example,
+it's not enough to tell the cell's new parents that the cell is now their child
+(the third task).  First we have to tell the cell's <EM>old</EM> parents that this cell <EM>isn't</EM> their child any more.  That
+has to be done before we forget the cell's old parents.
+
+<P>Here is an example.  (In the following tables, we represent the data structures
+as if cells were represented by their names, even though really their IDs
+are used.  We've done this to make the example more readable.)  Suppose that
+we have five cells set up like this:
+
+<P><TABLE><TR><TH>cell&nbsp;&nbsp;<TH>expression&nbsp;&nbsp;<TH>value&nbsp;&nbsp;
+<TH>parents&nbsp;&nbsp;<TH>children
+<TR><TD>&nbsp;&nbsp;<TR><TD><CODE>a1</CODE><TD><CODE>20</CODE>
+<TD><CODE>20</CODE><TD><CODE>()</CODE><TD><CODE>(a2)</CODE>
+<TR><TD><CODE>b1</CODE><TD><CODE>5</CODE><TD><CODE>5</CODE><TD><CODE>()</CODE>
+<TD><CODE>(a2 b2)</CODE>
+<TR><TD><CODE>c1</CODE><TD><CODE>8</CODE><TD><CODE>8</CODE><TD><CODE>()</CODE>
+<TD><CODE>()</CODE>
+<TR><TD><CODE>a2</CODE><TD><CODE>(+ a1 b1)</CODE><TD><CODE>25</CODE>
+<TD><CODE>(a1 b1)</CODE><TD><CODE>(b2)</CODE>
+<TR><TD><CODE>b2</CODE><TD><CODE>(+ a2 b1)</CODE><TD><CODE>30</CODE>
+<TD><CODE>(a2 b1)</CODE><TD><CODE>()</CODE>
+</TABLE>
+
+
+<P>If we now enter the spreadsheet command
+
+<P><PRE>(put (+ b1 c1) a2)
+</PRE>
+
+<P>the program must first remove <CODE>a2</CODE> from the children of its
+old parents (changes are shown in boldface):
+
+<P><TABLE><TR><TH>cell&nbsp;&nbsp;<TH>expression&nbsp;&nbsp;<TH>value&nbsp;&nbsp;
+<TH>parents&nbsp;&nbsp;<TH>children
+<TR><TD>&nbsp;&nbsp;<TR><TD><CODE>a1</CODE><TD><CODE>20</CODE>
+<TD><CODE>20</CODE><TD><CODE>()</CODE><TD><CODE><STRONG><BIG>()</BIG></STRONG></CODE>
+<TR><TD><CODE>b1</CODE><TD><CODE>5</CODE><TD><CODE>5</CODE><TD><CODE>()</CODE>
+<TD><CODE><STRONG><BIG>(b2)</BIG></STRONG></CODE>
+<TR><TD><CODE>c1</CODE><TD><CODE>8</CODE><TD><CODE>8</CODE><TD><CODE>()</CODE>
+<TD><CODE>()</CODE>
+<TR><TD><CODE>a2</CODE><TD><CODE>(+ a1 b1)</CODE><TD><CODE>25</CODE>
+<TD><CODE>(a1 b1)</CODE><TD><CODE>(b2)</CODE>
+<TR><TD><CODE>b2</CODE><TD><CODE>(+ a2 b1)</CODE><TD><CODE>30</CODE>
+<TD><CODE>(a2 b1)</CODE><TD><CODE>()</CODE>
+</TABLE>
+
+
+<P>Then the program can change the expression and compute a new list of
+parents:
+
+<P>
+<TABLE><TR><TH>cell&nbsp;&nbsp;<TH>expression&nbsp;&nbsp;<TH>value&nbsp;&nbsp;
+<TH>parents&nbsp;&nbsp;<TH>children
+<TR><TD>&nbsp;&nbsp;<TR><TD><CODE>a1</CODE><TD><CODE>20</CODE>
+<TD><CODE>20</CODE><TD><CODE>()</CODE><TD><CODE>()</CODE>
+<TR><TD><CODE>b1</CODE><TD><CODE>5</CODE><TD><CODE>5</CODE><TD><CODE>()</CODE>
+<TD><CODE>(b2)</CODE>
+<TR><TD><CODE>c1</CODE><TD><CODE>8</CODE><TD><CODE>8</CODE><TD><CODE>()</CODE>
+<TD><CODE>()</CODE>
+<TR><TD><CODE>a2</CODE><TD><CODE><STRONG><BIG>(+ b1 c1)</BIG></STRONG></CODE>
+<TD><CODE>25</CODE><TD><CODE><STRONG><BIG>(b1 c1)</BIG></STRONG></CODE>
+<TD><CODE>(b2)</CODE>
+<TR><TD><CODE>b2</CODE><TD><CODE>(+ a2 b1)</CODE><TD><CODE>30</CODE>
+<TD><CODE>(a2 b1)</CODE><TD><CODE>()</CODE>
+</TABLE>
+
+
+<P>Next it can tell the new parents to add <CODE>a2</CODE> as a
+child, and can compute <CODE>a2</CODE>'s new value:
+
+<P><TABLE><TR><TH>cell&nbsp;&nbsp;<TH>expression&nbsp;&nbsp;<TH>value&nbsp;&nbsp;
+<TH>parents&nbsp;&nbsp;<TH>children
+<TR><TD>&nbsp;&nbsp;<TR><TD><CODE>a1</CODE><TD><CODE>20</CODE>
+<TD><CODE>20</CODE><TD><CODE>()</CODE><TD><CODE>()</CODE>
+<TR><TD><CODE>b1</CODE><TD><CODE>5</CODE><TD><CODE>5</CODE><TD><CODE>()</CODE>
+<TD><CODE><STRONG><BIG>(a2 b2)</BIG></STRONG></CODE>
+<TR><TD><CODE>c1</CODE><TD><CODE>8</CODE><TD><CODE>8</CODE><TD><CODE>()</CODE>
+<TD><CODE><STRONG><BIG>(a2)</BIG></STRONG></CODE>
+<TR><TD><CODE>a2</CODE><TD><CODE>(+ b1 c1)</CODE>
+<TD><CODE><STRONG><BIG>13</BIG></STRONG></CODE><TD><CODE>(b1 c1)</CODE>
+<TD><CODE>(b2)</CODE>
+<TR><TD><CODE>b2</CODE><TD><CODE>(+ a2 b1)</CODE><TD><CODE>30</CODE>
+<TD><CODE>(a2 b1)</CODE><TD><CODE>()</CODE>
+</TABLE>
+
+
+<P>Changing <CODE>a2</CODE>'s value affects the values of all of its
+children, and also its grandchildren and so on (except that in this example
+there are no grandchildren):
+
+<P><TABLE><TR><TH>cell&nbsp;&nbsp;<TH>expression&nbsp;&nbsp;<TH>value&nbsp;&nbsp;
+<TH>parents&nbsp;&nbsp;<TH>children
+<TR><TD>&nbsp;&nbsp;<TR><TD><CODE>a1</CODE><TD><CODE>20</CODE>
+<TD><CODE>20</CODE><TD><CODE>()</CODE><TD><CODE>()</CODE>
+<TR><TD><CODE>b1</CODE><TD><CODE>5</CODE><TD><CODE>5</CODE><TD><CODE>()</CODE>
+<TD><CODE>(a2 b2)</CODE>
+<TR><TD><CODE>c1</CODE><TD><CODE>8</CODE><TD><CODE>8</CODE><TD><CODE>()</CODE>
+<TD><CODE>(a2)</CODE>
+<TR><TD><CODE>a2</CODE><TD><CODE>(+ b1 c1)</CODE>
+<TD><CODE>13</CODE><TD><CODE>(b1 c1)</CODE>
+<TD><CODE>(b2)</CODE>
+<TR><TD><CODE>b2</CODE><TD><CODE>(+ a2 b1)</CODE>
+<TD><CODE><STRONG><BIG>18</BIG></STRONG></CODE><TD><CODE>(a2 b1)</CODE><TD><CODE>()</CODE>
+</TABLE>
+
+
+<P>Now that we've considered an example, here is the main procedure that
+oversees all these tasks:
+
+<P><PRE>(define (<A NAME="g23"></A>put-expr expr-or-out-of-bounds id)
+  (let ((expr (if (equal? expr-or-out-of-bounds 'out-of-bounds)
+		  '()
+		  expr-or-out-of-bounds)))
+    (for-each (lambda (old-parent)
+		(set-cell-children!
+		 old-parent
+		 (remove id (cell-children old-parent))))
+	      (cell-parents id))
+    (set-cell-expr! id expr)
+    (set-cell-parents! id (remdup (extract-ids expr)))
+    (for-each (lambda (new-parent)
+		(set-cell-children!
+		 new-parent
+		 (cons id (cell-children new-parent))))
+	      (cell-parents id))
+    (figure id)))
+</PRE>
+ 
+Remember that <CODE>put-expr</CODE>'s first argument is the return value
+from <CODE>pin-down</CODE>, so it might be the word <CODE>out-of-bounds</CODE> instead of
+an expression.  In this case, we store an empty list as the expression,
+indicating that there is no active expression for this cell.
+
+<P>Within the body of the <CODE>let</CODE> there are five Scheme expressions, each of
+which carries out one of the tasks we've listed.  The first expression tells
+the cell's former parents that the cell is no longer their child.  The second
+expression stores the expression in the cell.
+
+<P>The third Scheme expression invokes <CODE>extract-ids</CODE> to find all the cell
+ids used in <CODE>expr</CODE>, removes any duplicates, and establishes those
+identified cells as the argument cell's parents.  (You wrote <CODE>remdup</CODE> in
+Exercise <A HREF="../ssch14/recur-patterns.html#remdup">14.3</A>.)  For example, if the <CODE>expr</CODE> is
+
+<P><PRE>(+ (id 4 2) (* (id 1 3) (id 1 3)))
+</PRE>
+
+<P>then <CODE>extract-ids</CODE> will return the list
+
+<P><PRE>((id 4 2) (id 1 3) (id 1 3))
+</PRE>
+
+<P>and <CODE>remdup</CODE> of that will be
+
+<P><PRE>((id 4 2) (id 1 3))
+</PRE>
+
+<P>The fourth expression in the <CODE>let</CODE> tells each of the new parents to
+consider the argument cell as its child.  The fifth expression may or may
+not compute a new value for this cell.  (As we'll see in a moment, that
+process is a little complicated.)
+
+<P>Two of these steps require closer examination.  Here is the procedure used
+in the third step:
+
+<P><PRE>(define (<A NAME="g24"></A>extract-ids expr)
+  (cond ((id? expr) (list expr))
+	((word? expr) '())
+	((null? expr) '())
+	(else (append (extract-ids (car expr))
+		      (extract-ids (cdr expr))))))
+</PRE>
+
+<P>This is a tree recursion.  The first three <CODE>cond</CODE> clauses are
+base cases; cell IDs are included in the returned list, while other words
+are ignored.  For compound expressions, we use the trick of making recursive
+calls on the <CODE>car</CODE> and <CODE>cdr</CODE> of the list.  We combine the results
+with <CODE>append</CODE> because <CODE>extract-ids</CODE> must return a flat list of
+cell IDs, not a cheap tree.
+
+<P>The fifth step in <CODE>put-expr</CODE> is complicated because, as we saw in the
+example, changing the value of one cell may require us to recompute the
+value of other cells:
+
+<P><PRE>(define (<A NAME="g25"></A>figure id)
+  (cond ((null? (cell-expr id)) (setvalue id '()))
+	((all-evaluated? (cell-parents id))
+	 (setvalue id (ss-eval (cell-expr id))))
+	(else (setvalue id '()))))
+
+(define (<A NAME="g26"></A>all-evaluated? ids)
+  (cond ((null? ids) #t)
+	((not (number? (cell-value (car ids)))) #f)
+	(else (all-evaluated? (cdr ids)))))
+
+(define (<A NAME="g27"></A>setvalue id value)
+  (let ((old (cell-value id)))
+    (set-cell-value! id value)
+    (if (not (equal? old value))
+	(for-each figure (cell-children id))
+	'do-nothing)))
+</PRE>
+
+<P><CODE>Figure</CODE> is invoked for the cell whose expression we've just
+changed.  If there is no expression (that is, if we've changed it to an empty
+expression or to an out-of-bounds one), then <CODE>figure</CODE> will remove any old
+value that might be left over from a previous expression.  If there is an
+expression, then <CODE>figure</CODE> will compute and save a new value, but only if
+all of this cell's parents have numeric values.  If any parent doesn't have
+a value, or if its value is a non-numeric label, then <CODE>figure</CODE> has to
+remove the value from this cell.
+
+<P><CODE>Setvalue</CODE> actually puts the new value in the cell.  It first looks up
+the old value.  If the new and old values are different, then all of the
+children of this cell must be re-<CODE>figure</CODE>d.  This, too, is a tree
+recursion because there might be several children, and each of them might
+have several children.
+
+<P>We haven't yet explained how <CODE>ss-eval</CODE> actually computes the value from
+the expression.  That's the subject of the next major part of the program.
+
+<P><H2>The Expression Evaluator</H2>
+
+<P><CODE>Figure</CODE> invokes <CODE>ss-eval</CODE> to convert a cell's expression into its
+value.  Also, we've seen earlier that <CODE>process-command</CODE> uses <CODE>ss-eval</CODE> to evaluate an expression that the user types in response to a
+spreadsheet prompt.  (That is, <CODE>ss-eval</CODE> is invoked if what the user
+types isn't one of the special commands recognized by <CODE>process-command</CODE>
+itself.)
+
+<P>The <CODE>ss</CODE> in <CODE>ss-eval</CODE> stands for &quot;spreadsheet&quot;; it distinguishes
+this procedure from <A NAME="g28"></A><CODE>eval</CODE>, a primitive procedure that evaluates
+Scheme expressions.  As it turns out, <CODE>ss-eval</CODE>'s algorithm is
+similar in many ways to that of Scheme's <CODE>eval</CODE>, although <CODE>ss-eval</CODE>
+is much simpler in other ways.  The experience you already have with
+Scheme's expression evaluation will help you understand the spreadsheet's.
+
+<P>Scheme's evaluator takes an expression and computes the corresponding value.
+The expressions look quite different from the values, but there are
+well-defined rules (the ones we studied in Chapter 3) to translate
+expressions to values.  In the spreadsheet language, as in Scheme, an
+expression can be one of three things:
+
+<P><P><TABLE><TR><TH align="right" valign="top">&bull;<TD>&nbsp;&nbsp;&nbsp;&nbsp;<TD valign="top">a constant expression (a number or quoted word), whose value is itself.
+</TABLE><TABLE><TR><TH align="right" valign="top">&bull;<TD>&nbsp;&nbsp;&nbsp;&nbsp;<TD valign="top">a variable (a cell ID, in the case of the spreadsheet language).
+</TABLE><TABLE><TR><TH align="right" valign="top">&bull;<TD>&nbsp;&nbsp;&nbsp;&nbsp;<TD valign="top">a procedure invocation enclosed in parentheses.
+
+</TABLE><P>
+The spreadsheet language is simpler than Scheme for three main reasons.
+
+<P><P><TABLE><TR><TH align="right" valign="top">&bull;<TD>&nbsp;&nbsp;&nbsp;&nbsp;<TD valign="top">There are no special forms such as <CODE>if</CODE> or <CODE>define</CODE>.<A NAME="text5" HREF="spread-implement#ft5">[5]</A>
+</TABLE><TABLE><TR><TH align="right" valign="top">&bull;<TD>&nbsp;&nbsp;&nbsp;&nbsp;<TD valign="top">The only variables, the cell IDs, are global; in Scheme, much of the
+complexity of evaluation has to do with variables that are local to
+procedures (i.e., formal parameters).
+</TABLE><TABLE><TR><TH align="right" valign="top">&bull;<TD>&nbsp;&nbsp;&nbsp;&nbsp;<TD valign="top">The only procedures are primitives, so there is no need to evaluate
+procedure bodies.
+
+</TABLE><P>
+The structure of <CODE>ss-eval</CODE> is a <CODE>cond</CODE> whose clauses handle the 
+three types of expressions.  Constants and variables are easy;
+invocations require recursively evaluating the arguments before the
+procedure can be invoked.
+
+<P><PRE>(define (<A NAME="g29"></A>ss-eval expr)
+  (cond ((number? expr) expr)
+	((quoted? expr) (quoted-value expr))
+	((id? expr) (cell-value expr))
+	((invocation? expr)
+	 (apply (get-function (car expr))
+		(map ss-eval (cdr expr))))
+	(else (error &quot;Invalid expression:&quot; expr))))
+</PRE>
+
+<P>The value of a number is itself; the value of a quoted word is the word
+without the quotation mark.  (Actually, by the time <CODE>ss-eval</CODE> sees a
+quoted word, Scheme has translated the <CODE>'abc</CODE> notation into <CODE>(quote abc)</CODE> and that's what we deal with here.  Also, double-quoted strings
+look different to the program from single-quoted words.)
+
+<P><PRE>(define (<A NAME="g30"></A>quoted? expr)
+  (or (string? expr)
+      (and (list? expr) (equal? (car expr) 'quote))))
+
+(define (<A NAME="g31"></A>quoted-value expr)
+  (if (string? expr)
+      expr
+      (cadr expr)))
+</PRE>
+
+<P>The third clause checks for a cell ID; the value of such an
+expression is the value stored in the corresponding cell.
+
+<P>If an expression is none of those things, it had better be a function
+invocation, that is, a list.  In that case, <CODE>ss-eval</CODE> has to do three
+things:  It looks up the function name in a table (as we did earlier for
+spreadsheet commands); it recursively evaluates all the argument
+subexpressions; and then it can invoke <CODE>apply</CODE> to apply the procedure to
+the argument values.
+
+<P><CODE>Get-function</CODE> looks up a function name in the name-to-function
+association list and returns the corresponding Scheme procedure.  Thus, only
+the functions included in the list can be used in spreadsheet formulas.
+
+<P>The entire expression evaluator, including <CODE>ss-eval</CODE> and its helper
+procedures, is functional.  Like the formula translator, it doesn't change
+the state of the spreadsheet.
+
+<P><H2>The Screen Printer</H2>
+
+<P>The procedures that print the spreadsheet on the screen are straightforward
+but full of details.  Much of the work here goes into those details.
+
+<P>As we mentioned earlier, a better spreadsheet program wouldn't redraw the
+entire screen for each command but would change only  the parts of the
+screen that were affected by the previous command.  However, Scheme does not
+include a standard way to control the positioning of text on the screen, so
+we're stuck with this approach.
+
+<P><PRE>(define (<A NAME="g32"></A>print-screen)
+  (newline)
+  (newline)
+  (newline)
+  (show-column-labels (id-column (screen-corner-cell-id)))
+  (show-rows 20
+	     (id-column (screen-corner-cell-id))
+	     (id-row (screen-corner-cell-id)))
+  (display-cell-name (selection-cell-id))
+  (show (cell-value (selection-cell-id)))
+  (display-expression (cell-expr (selection-cell-id)))
+  (newline)
+  (display &quot;?? &quot;))
+</PRE>
+
+<P><CODE>Screen-corner-cell-id</CODE> returns the ID of the cell that
+should be shown in the top left corner of the display; <CODE>selection-cell-id</CODE> returns the ID of the selected cell.
+
+<P><CODE>Show-column-labels</CODE> prints the first row of the display, the one with
+the column letters.  <CODE>Show-rows</CODE> is a sequential recursion that prints
+the actual rows of the spreadsheet, starting with the row number of the <CODE>screen-corner</CODE> cell and continuing for 20 rows.  (There are 30 rows in the
+entire spreadsheet, but they don't all fit on the screen at once.)  The rest
+of the procedure displays the value and expression of the selected cell at
+the bottom of the screen and prompts for the next command.
+
+<P>Why isn't <CODE>display-expression</CODE> just <CODE>display</CODE>?
+Remember that the spreadsheet stores expressions in a form like
+
+<P><PRE>(+ (id 2 5) (id 6 3))
+</PRE>
+
+<P>but the user wants to see
+
+<P><PRE>(+ b5 f3)
+</PRE>
+
+<P><CODE>Display-expression</CODE> is yet another tree recursion over
+expressions.  Just as <CODE>pin-down</CODE> translates cell names into cell IDs,
+<CODE>display-expression</CODE> translates IDs back into names.  (But <CODE>display-expression</CODE> prints as it goes along, instead of reconstructing and
+returning a list.)  The definition of <CODE>display-expression</CODE>, along with
+the remaining details of printing, can be seen in the full program listing
+at the end of this chapter.
+
+<P>Just to give the flavor of those details, here is the part that displays
+the rectangular array of cell values.  <CODE>Show-rows</CODE> is a sequential
+recursion in which each invocation prints an entire row.  It does so by
+invoking <CODE>show-row</CODE>, another sequential recursion, in which each
+invocation prints a single cell value.
+
+<P><PRE>(define (show-rows to-go col row)
+  (cond ((= to-go 0) 'done)
+	(else
+	 (display (align row 2 0))
+	 (display &quot; &quot;)
+	 (show-row 6 col row)
+	 (newline)
+	 (show-rows (- to-go 1) col (+ row 1)))))
+
+(define (show-row to-go col row)
+  (cond ((= to-go 0) 'done)
+	(else
+	   (display (if (selected-indices? col row) &quot;&gt;&quot; &quot; &quot;))
+	   (display-value (cell-value-from-indices col row))
+	   (display (if (selected-indices? col row) &quot;&lt;&quot; &quot; &quot;))
+	   (show-row (- to-go 1) (+ 1 col) row))))
+
+(define (selected-indices? col row)
+  (and (= col (id-column (selection-cell-id)))
+       (= row (id-row (selection-cell-id)))))
+</PRE>
+
+<P>Why didn't we write <CODE>show-row</CODE> in the following way?
+
+<P><PRE>(define (show-row to-go col row)             ;; alternate version
+  (cond ((= to-go 0) 'done)
+	(else
+	 (let ((id (make-id col row)))
+	   (display (if (equal? id (selection-cell-id)) &quot;&gt;&quot; &quot; &quot;))
+	   (display-value (cell-value id))
+	   (display (if (equal? id (selection-cell-id)) &quot;&lt;&quot; &quot; &quot;))
+	   (show-row (- to-go 1) (+ 1 col) row)))))
+</PRE>
+
+<P>That would have worked fine and would have been a little clearer.
+In fact, we did write <CODE>show-row</CODE> this way originally.  But it's a little
+time-consuming to construct an ID, and <CODE>show-row</CODE> is called 120 times
+whenever <CODE>print-screen</CODE> is used.  Since printing the screen was
+annoyingly slow, we sped things up as much as we could, even at the cost of
+this kludge.
+
+<P><H2>The Cell Manager</H2>
+
+<P>The program keeps information about the current status of the spreadsheet
+cells in a vector called <CODE>*the-spreadsheet-array*</CODE>.  It contains all of
+the 780 cells that make up the spreadsheet (30 rows of 26 columns).  It's
+not a vector of length 780; rather, it's a vector of length 30, each of whose
+elements is itself a vector of length 26.  In other words, each element of
+the spreadsheet array is a vector representing one row of the spreadsheet.
+(Each element of <EM>those</EM> vectors is one cell, which, as you recall, is
+represented as a vector of length four.  So the spreadsheet array is a
+vector of vectors of vectors!)
+
+<P>The selectors for the parts of a cell take the cell's ID as argument and
+return one of the four elements of the cell vector.  Each must therefore be
+implemented as two steps:  We must find the cell vector, given its ID; and
+we must then select an element from the cell vector.  The first step is
+handled by the selector <CODE>cell-structure</CODE> that takes a cell ID as
+argument:
+
+<P><PRE>(define (<A NAME="g33"></A>cell-structure id)
+  (global-array-lookup (id-column id)
+		       (id-row id)))
+
+(define (<A NAME="g34"></A>global-array-lookup col row)
+  (if (and (&lt;= row 30) (&lt;= col 26))
+      (vector-ref (vector-ref *the-spreadsheet-array* (- row 1))
+		  (- col 1))
+      (error &quot;Out of bounds&quot;)))
+</PRE>
+
+<P><CODE>Global-array-lookup</CODE> makes sure the desired cell exists.
+It also compensates for the fact that Scheme vectors begin with element
+number zero, while our spreadsheet begins with row and column number one.
+Two invocations of <CODE>vector-ref</CODE> are needed, one to select an entire row
+and the next to select a cell within that row.
+
+<P>Selectors and mutators for the parts of a cell are written using
+<CODE>cell-structure</CODE>:
+
+<P><PRE>(define (<A NAME="g35"></A>cell-value id)
+  (vector-ref (cell-structure id) 0))
+
+(define (<A NAME="g36"></A>set-cell-value! id val)
+  (vector-set! (cell-structure id) 0 val))
+
+(define (<A NAME="g37"></A>cell-expr id)
+  (vector-ref (cell-structure id) 1))
+
+(define (<A NAME="g38"></A>set-cell-expr! id val)
+  (vector-set! (cell-structure id) 1 val))
+
+(define (<A NAME="g39"></A>cell-parents id)
+  (vector-ref (cell-structure id) 2))
+
+(define (<A NAME="g40"></A>set-cell-parents! id val)
+  (vector-set! (cell-structure id) 2 val))
+
+(define (<A NAME="g41"></A>cell-children id)
+  (vector-ref (cell-structure id) 3))
+
+(define (<A NAME="g42"></A>set-cell-children! id val)
+  (vector-set! (cell-structure id) 3 val))
+</PRE>
+
+<P>The constructor is
+
+<P><PRE>(define (make-cell)
+  (vector '() '() '() '()))
+</PRE>
+
+<P>The spreadsheet program begins by invoking <CODE>init-array</CODE> to set up this
+large array.  (Also, it sets the initial values of the selected cell and the
+screen corner.)
+
+<P><PRE>(define (<A NAME="g43"></A>spreadsheet)
+  (init-array)
+  (set-selection-cell-id! (make-id 1 1))
+  (set-screen-corner-cell-id! (make-id 1 1))
+  (command-loop))
+
+(define *the-spreadsheet-array* (make-vector 30))
+
+(define (<A NAME="g44"></A>init-array)
+  (fill-array-with-rows 29))
+
+(define (<A NAME="g45"></A>fill-array-with-rows n)
+  (if (&lt; n 0)
+      'done
+      (begin (vector-set! *the-spreadsheet-array* n (make-vector 26))
+	     (fill-row-with-cells
+	      (vector-ref *the-spreadsheet-array* n) 25)
+	     (fill-array-with-rows (- n 1)))))
+
+(define (<A NAME="g46"></A>fill-row-with-cells vec n)
+  (if (&lt; n 0)
+      'done
+      (begin (vector-set! vec n (make-cell))
+	     (fill-row-with-cells vec (- n 1)))))
+</PRE>
+
+<P>
+<P>That's the end of the project, apart from some straightforward procedures
+such as <CODE>letter-&gt;number</CODE> that you can look up in the complete listing
+if you're interested.
+
+<P><H2>Complete Program Listing</H2>
+
+<P><P><P><PRE>
+(define (spreadsheet)
+  (init-array)
+  (set-selection-cell-id! (make-id 1 1))
+  (set-screen-corner-cell-id! (make-id 1 1))
+  (command-loop))
+
+(define (command-loop)
+  (print-screen)
+  (let ((command-or-formula (read)))
+    (if (equal? command-or-formula 'exit)
+	"Bye!"
+	(begin (process-command command-or-formula)
+	       (command-loop)))))
+
+(define (process-command command-or-formula)
+  (cond ((and (list? command-or-formula)
+	      (command? (car command-or-formula)))
+	 (execute-command command-or-formula))
+	((command? command-or-formula)
+	 (execute-command (list command-or-formula 1)))
+	(else (exhibit (ss-eval (pin-down command-or-formula
+					  (selection-cell-id)))))))
+
+(define (execute-command command)
+  (apply (get-command (car command))
+	 (cdr command)))
+
+(define (exhibit val)
+  (show val)
+  (show "Type RETURN to redraw screen")
+  (read-line)
+  (read-line))
+
+
+;;; Commands
+
+;; Cell selection commands: F, B, N, P, and SELECT
+
+(define (prev-row delta)
+  (let ((row (id-row (selection-cell-id))))
+    (if (< (- row delta) 1)
+	(error "Already at top.")
+	(set-selected-row! (- row delta)))))
+
+(define (next-row delta)
+  (let ((row (id-row (selection-cell-id))))
+    (if (> (+ row delta) 30)
+	(error "Already at bottom.")
+	(set-selected-row! (+ row delta)))))
+
+(define (prev-col delta)
+  (let ((col (id-column (selection-cell-id))))
+    (if (< (- col delta) 1)
+	(error "Already at left.")
+	(set-selected-column! (- col delta)))))
+
+(define (next-col delta)
+  (let ((col (id-column (selection-cell-id))))
+    (if (> (+ col delta) 26)
+	(error "Already at right.")
+	(set-selected-column! (+ col delta)))))
+
+(define (set-selected-row! new-row)
+  (select-id! (make-id (id-column (selection-cell-id)) new-row)))
+
+(define (set-selected-column! new-column)
+  (select-id! (make-id new-column (id-row (selection-cell-id)))))
+
+(define (select-id! id)
+  (set-selection-cell-id! id)
+  (adjust-screen-boundaries))
+
+(define (select cell-name)
+  (select-id! (cell-name->id cell-name)))
+
+(define (adjust-screen-boundaries)
+  (let ((row (id-row (selection-cell-id)))
+	(col (id-column (selection-cell-id))))
+    (if (< row (id-row (screen-corner-cell-id)))
+	(set-corner-row! row)
+	'do-nothing)
+    (if (>= row (+ (id-row (screen-corner-cell-id)) 20))
+	(set-corner-row! (- row 19))
+	'do-nothing)
+    (if (< col (id-column (screen-corner-cell-id)))
+	(set-corner-column! col)
+	'do-nothing)
+    (if (>= col (+ (id-column (screen-corner-cell-id)) 6))
+	(set-corner-column! (- col 5))
+	'do-nothing)))
+
+(define (set-corner-row! new-row)
+  (set-screen-corner-cell-id!
+   (make-id (id-column (screen-corner-cell-id)) new-row)))
+
+(define (set-corner-column! new-column)
+  (set-screen-corner-cell-id!
+   (make-id new-column (id-row (screen-corner-cell-id)))))
+
+
+;; LOAD
+
+(define (spreadsheet-load filename)
+  (let ((port (open-input-file filename)))
+    (sl-helper port)
+    (close-input-port port)))
+
+(define (sl-helper port)
+  (let ((command (read port)))
+    (if (eof-object? command)
+	'done
+	(begin (show command)
+	       (process-command command)
+	       (sl-helper port)))))
+
+
+;; PUT
+
+(define (put formula . where)
+  (cond ((null? where)
+	 (put-formula-in-cell formula (selection-cell-id)))
+	((cell-name? (car where))
+	 (put-formula-in-cell formula (cell-name->id (car where))))
+	((number? (car where))
+	 (put-all-cells-in-row formula (car where)))
+	((letter? (car where))
+	 (put-all-cells-in-col formula (letter->number (car where))))
+	(else (error "Put it where?"))))
+
+(define (put-all-cells-in-row formula row)
+  (put-all-helper formula (lambda (col) (make-id col row)) 1 26))
+
+(define (put-all-cells-in-col formula col)
+  (put-all-helper formula (lambda (row) (make-id col row)) 1 30))
+
+(define (put-all-helper formula id-maker this max)
+  (if (> this max)
+      'done
+      (begin (try-putting formula (id-maker this))
+	     (put-all-helper formula id-maker (+ 1 this) max))))
+
+(define (try-putting formula id)
+  (if (or (null? (cell-value id)) (null? formula))
+      (put-formula-in-cell formula id)
+      'do-nothing))
+
+(define (put-formula-in-cell formula id)
+  (put-expr (pin-down formula id) id))
+
+
+;;; The Association List of Commands
+
+(define (command? name)
+  (assoc name *the-commands*))
+
+(define (get-command name)
+  (let ((result (assoc name *the-commands*)))
+    (if (not result)
+	#f
+	(cadr result))))
+
+(define *the-commands*
+  (list (list 'p prev-row)
+	(list 'n next-row)
+	(list 'b prev-col)
+	(list 'f next-col)
+	(list 'select select)
+	(list 'put put)
+	(list 'load spreadsheet-load)))
+
+
+;;; Pinning Down Formulas Into Expressions
+
+(define (pin-down formula id)
+  (cond ((cell-name? formula) (cell-name->id formula))
+	((word? formula) formula)
+	((null? formula) '())
+	((equal? (car formula) 'cell)
+	 (pin-down-cell (cdr formula) id))
+	(else (bound-check
+	       (map (lambda (subformula) (pin-down subformula id))
+		    formula)))))
+
+(define (bound-check form)
+  (if (member 'out-of-bounds form)
+      'out-of-bounds
+      form))
+
+(define (pin-down-cell args reference-id)
+  (cond ((null? args)
+	 (error "Bad cell specification: (cell)"))
+	((null? (cdr args))
+	 (cond ((number? (car args))         ; they chose a row
+		(make-id (id-column reference-id) (car args)))
+	       ((letter? (car args))         ; they chose a column
+		(make-id (letter->number (car args))
+			 (id-row reference-id)))
+	       (else (error "Bad cell specification:"
+			    (cons 'cell args)))))
+	(else
+	 (let ((col (pin-down-col (car args) (id-column reference-id)))
+	       (row (pin-down-row (cadr args) (id-row reference-id))))
+	   (if (and (>= col 1) (<= col 26) (>= row 1) (<= row 30))
+	       (make-id col row)
+	       'out-of-bounds)))))
+
+(define (pin-down-col new old)
+  (cond ((equal? new '*) old)
+	((equal? (first new) '>) (+ old (bf new)))
+	((equal? (first new) '<) (- old (bf new)))
+	((letter? new) (letter->number new))
+	(else (error "What column?"))))
+
+(define (pin-down-row new old)
+  (cond ((number? new) new)
+	((equal? new '*) old)
+	((equal? (first new) '>) (+ old (bf new)))
+	((equal? (first new) '<) (- old (bf new)))
+	(else (error "What row?"))))
+
+
+;;; Dependency Management
+
+(define (put-expr expr-or-out-of-bounds id)
+  (let ((expr (if (equal? expr-or-out-of-bounds 'out-of-bounds)
+		  '()
+		  expr-or-out-of-bounds)))
+    (for-each (lambda (old-parent)
+		(set-cell-children!
+		 old-parent
+		 (remove id (cell-children old-parent))))
+	      (cell-parents id))
+    (set-cell-expr! id expr)
+    (set-cell-parents! id (remdup (extract-ids expr)))
+    (for-each (lambda (new-parent)
+		(set-cell-children!
+		 new-parent
+		 (cons id (cell-children new-parent))))
+	      (cell-parents id))
+    (figure id)))
+
+(define (extract-ids expr)
+  (cond ((id? expr) (list expr))
+	((word? expr) '())
+	((null? expr) '())
+	(else (append (extract-ids (car expr))
+		      (extract-ids (cdr expr))))))
+
+(define (figure id)
+  (cond ((null? (cell-expr id)) (setvalue id '()))
+	((all-evaluated? (cell-parents id))
+	 (setvalue id (ss-eval (cell-expr id))))
+	(else (setvalue id '()))))
+
+(define (all-evaluated? ids)
+  (cond ((null? ids) #t)
+	((not (number? (cell-value (car ids)))) #f)
+	(else (all-evaluated? (cdr ids)))))
+
+(define (setvalue id value)
+  (let ((old (cell-value id)))
+    (set-cell-value! id value)
+    (if (not (equal? old value))
+	(for-each figure (cell-children id))
+	'do-nothing)))
+
+
+;;; Evaluating Expressions
+
+(define (ss-eval expr)
+  (cond ((number? expr) expr)
+	((quoted? expr) (quoted-value expr))
+	((id? expr) (cell-value expr))
+	((invocation? expr)
+	 (apply (get-function (car expr))
+		(map ss-eval (cdr expr))))
+	(else (error "Invalid expression:" expr))))
+
+(define (quoted? expr)
+  (or (string? expr)
+      (and (list? expr) (equal? (car expr) 'quote))))
+
+(define (quoted-value expr)
+  (if (string? expr)
+      expr
+      (cadr expr)))
+
+(define (invocation? expr)
+  (list? expr))
+
+(define (get-function name)
+  (let ((result (assoc name *the-functions*)))
+    (if (not result)
+	(error "No such function: " name)
+	(cadr result))))
+
+(define *the-functions*
+  (list (list '* *)
+	(list '+ +)
+	(list '- -)
+	(list '/ /)
+	(list 'abs abs)
+	(list 'acos acos)
+	(list 'asin asin)
+	(list 'atan atan)
+	(list 'ceiling ceiling)
+	(list 'cos cos)
+	(list 'count count)
+	(list 'exp exp)
+	(list 'expt expt)
+	(list 'floor floor)
+	(list 'gcd gcd)
+	(list 'lcm lcm)
+	(list 'log log)
+	(list 'max max)
+	(list 'min min)
+	(list 'modulo modulo)
+	(list 'quotient quotient)
+	(list 'remainder remainder)
+	(list 'round round)
+	(list 'sin sin)
+	(list 'sqrt sqrt)
+	(list 'tan tan)
+	(list 'truncate truncate)))
+
+;;; Printing the Screen
+
+(define (print-screen)
+  (newline)
+  (newline)
+  (newline)
+  (show-column-labels (id-column (screen-corner-cell-id)))
+  (show-rows 20
+	     (id-column (screen-corner-cell-id))
+	     (id-row (screen-corner-cell-id)))
+  (display-cell-name (selection-cell-id))
+  (display ":  ")
+  (show (cell-value (selection-cell-id)))
+  (display-expression (cell-expr (selection-cell-id)))
+  (newline)
+  (display "?? "))
+
+(define (display-cell-name id)
+  (display (number->letter (id-column id)))
+  (display (id-row id)))
+
+(define (show-column-labels col-number)
+  (display "  ")
+  (show-label 6 col-number)
+  (newline))
+
+(define (show-label to-go this-col-number)
+  (cond ((= to-go 0) '())
+	(else
+	 (display "  -----")
+	 (display (number->letter this-col-number))
+	 (display "----")
+	 (show-label (- to-go 1) (+ 1 this-col-number)))))
+
+(define (show-rows to-go col row)
+  (cond ((= to-go 0) 'done)
+	(else
+	 (display (align row 2 0))
+	 (display " ")
+	 (show-row 6 col row)
+	 (newline)
+	 (show-rows (- to-go 1) col (+ row 1)))))
+
+(define (show-row to-go col row)
+  (cond ((= to-go 0) 'done)
+	(else
+	   (display (if (selected-indices? col row) ">" " "))
+	   (display-value (cell-value-from-indices col row))
+	   (display (if (selected-indices? col row) "<" " "))
+	   (show-row (- to-go 1) (+ 1 col) row))))
+
+(define (selected-indices? col row)
+  (and (= col (id-column (selection-cell-id)))
+       (= row (id-row (selection-cell-id)))))
+
+(define (display-value val)
+  (display (align (if (null? val) "" val) 10 2)))
+
+(define (display-expression expr)
+  (cond ((null? expr) (display '()))
+	((quoted? expr) (display (quoted-value expr)))
+	((word? expr) (display expr))
+	((id? expr)
+	 (display-cell-name expr))
+	(else (display-invocation expr))))
+
+(define (display-invocation expr)
+  (display "(")
+  (display-expression (car expr))
+  (for-each (lambda (subexpr)
+	      (display " ")
+	      (display-expression subexpr))
+	    (cdr expr))
+  (display ")"))
+      
+
+;;; Abstract Data Types
+
+;; Special cells: the selected cell and the screen corner
+
+(define *special-cells* (make-vector 2))
+
+(define (selection-cell-id)
+  (vector-ref *special-cells* 0))
+
+(define (set-selection-cell-id! new-id)
+  (vector-set! *special-cells* 0 new-id))
+
+(define (screen-corner-cell-id)
+  (vector-ref *special-cells* 1))
+
+(define (set-screen-corner-cell-id! new-id)
+  (vector-set! *special-cells* 1 new-id))
+
+
+;; Cell names
+
+(define (cell-name? expr)
+  (and (word? expr)
+       (letter? (first expr))
+       (number? (bf expr))))
+
+(define (cell-name-column cell-name)
+  (letter->number (first cell-name)))
+
+(define (cell-name-row cell-name)
+  (bf cell-name))
+
+(define (cell-name->id cell-name)
+  (make-id (cell-name-column cell-name)
+	   (cell-name-row cell-name)))
+	
+;; Cell IDs
+
+(define (make-id col row)
+  (list 'id col row))
+
+(define (id-column id)
+  (cadr id))
+
+(define (id-row id)
+  (caddr id))
+
+(define (id? x)
+  (and (list? x)
+       (not (null? x))
+       (equal? 'id (car x))))
+
+;; Cells
+
+(define (make-cell)
+  (vector '() '() '() '()))
+
+(define (cell-value id)
+  (vector-ref (cell-structure id) 0))
+
+(define (cell-value-from-indices col row)
+  (vector-ref (cell-structure-from-indices col row) 0))
+
+(define (cell-expr id)
+  (vector-ref (cell-structure id) 1))
+
+(define (cell-parents id)
+  (vector-ref (cell-structure id) 2))
+
+(define (cell-children id)
+  (vector-ref (cell-structure id) 3))
+
+(define (set-cell-value! id val)
+  (vector-set! (cell-structure id) 0 val))
+
+(define (set-cell-expr! id val)
+  (vector-set! (cell-structure id) 1 val))
+
+(define (set-cell-parents! id val)
+  (vector-set! (cell-structure id) 2 val))
+
+(define (set-cell-children! id val)
+  (vector-set! (cell-structure id) 3 val))
+
+(define (cell-structure id)
+  (global-array-lookup (id-column id)
+		       (id-row id)))
+
+(define (cell-structure-from-indices col row)
+  (global-array-lookup col row))
+
+(define *the-spreadsheet-array* (make-vector 30))
+
+(define (global-array-lookup col row)
+  (if (and (<= row 30) (<= col 26))
+      (vector-ref (vector-ref *the-spreadsheet-array* (- row 1))
+		  (- col 1))
+      (error "Out of bounds")))
+
+(define (init-array)
+  (fill-array-with-rows 29))
+
+(define (fill-array-with-rows n)
+  (if (< n 0)
+      'done
+      (begin (vector-set! *the-spreadsheet-array* n (make-vector 26))
+	     (fill-row-with-cells
+	      (vector-ref *the-spreadsheet-array* n) 25)
+	     (fill-array-with-rows (- n 1)))))
+
+(define (fill-row-with-cells vec n)
+  (if (< n 0)
+      'done
+      (begin (vector-set! vec n (make-cell))
+	     (fill-row-with-cells vec (- n 1)))))
+
+;;; Utility Functions
+
+(define alphabet
+  '#(a b c d e f g h i j k l m n o p q r s t u v w x y z))
+
+(define (letter? something)
+  (and (word? something)
+       (= 1 (count something))
+       (vector-member something alphabet)))
+
+(define (number->letter num)
+  (vector-ref alphabet (- num 1)))
+
+(define (letter->number letter)
+  (+ (vector-member letter alphabet) 1))
+
+(define (vector-member thing vector)
+  (vector-member-helper thing vector 0))
+
+(define (vector-member-helper thing vector index)
+  (cond ((= index (vector-length vector)) #f)
+	((equal? thing (vector-ref vector index)) index)
+	(else (vector-member-helper thing vector (+ 1 index)))))
+
+(define (remdup lst)
+  (cond ((null? lst) '())
+	((member (car lst) (cdr lst))
+	 (remdup (cdr lst)))
+	(else (cons (car lst) (remdup (cdr lst))))))
+
+(define (remove bad-item lst)
+  (filter (lambda (item) (not (equal? item bad-item)))
+	  lst))
+</PRE><P>
+
+
+
+<P><H2>Exercises</H2>
+
+<P><B>25.1</B>&nbsp;&nbsp;The &quot;magic numbers&quot; 26 and 30 (and some numbers derived from
+them) appear many times in the text of this program.  It's easy to imagine
+wanting more rows or columns.
+
+<P>Create global variables <CODE>total-cols</CODE> and <CODE>total-rows</CODE> with values 26
+and 30 respectively.  Then modify the spreadsheet program to refer to these
+variables rather than to the numbers 26 and 30 directly.  When you're done,
+redefine <CODE>total-rows</CODE> to be 40 and see if it works.
+
+
+<P>
+<B>25.2</B>&nbsp;&nbsp;Suggest a way to notate columns beyond <CODE>z</CODE>.  What procedures would have
+to change to accommodate this?
+
+
+<P>
+<B>25.3</B>&nbsp;&nbsp;Modify the program so that the spreadsheet array is kept as a single vector
+of 780 elements, instead of a vector of 30 vectors of 26 vectors.  What
+procedures do you have to change to make this work?  (It shouldn't be very
+many.)
+
+
+<P>
+<B>25.4</B>&nbsp;&nbsp;The procedures <CODE>get-function</CODE> and <CODE>get-command</CODE> are almost identical
+in structure; both look for an argument in an association list.  They
+differ, however, in their handling of the situation in which the argument is
+not present in the list.  Why?
+
+
+<P>
+<B>25.5</B>&nbsp;&nbsp;The reason we had to include the word <CODE>id</CODE> in each cell ID was so we
+would be able to distinguish a list representing a cell ID from a list of
+some other kind in an expression.  Another way to distinguish cell IDs would
+be to represent them as vectors, since vectors do not otherwise appear
+within expressions.  Change the implementation of cell IDs from
+three-element lists to two-element vectors:
+
+<P><PRE>&gt; (make-id 4 2)
+#(4 2)
+</PRE>
+
+<P>Make sure the rest of the program still works.
+
+
+<P>
+<B>25.6</B>&nbsp;&nbsp;The <CODE>put</CODE> command can be used to label a cell by using a quoted word
+as the &quot;formula.&quot; How does that work?  For example, how is such a formula
+translated into an expression?  How is that expression evaluated?  What if
+the labeled cell has children?
+
+
+<P>
+<B>25.7</B>&nbsp;&nbsp;Add commands to move the &quot;window&quot; of cells displayed on the screen
+without changing the selected cell.  (There are a lot of possible user
+interfaces for this feature; pick anything reasonable.)
+
+
+<P>
+<B>25.8</B>&nbsp;&nbsp;Modify the <CODE>put</CODE> command so that after doing its work it prints
+
+<P><PRE>14 cells modified
+</PRE>
+
+<P>(but, of course, using the actual number of cells modified
+instead of 14).  This number may not be the entire length of a row or column
+because <CODE>put</CODE> doesn't change an existing formula in a cell when you ask
+it to set an entire row or column.
+
+
+<P>
+<B>25.9</B>&nbsp;&nbsp;Modify the program so that each column remembers the number of digits that
+should be displayed after the decimal point (currently always 2).  Add a
+command to set this value for a specified column.  And, of course, modify
+<CODE>print-screen</CODE> to use this information.
+
+
+<P>
+<B>25.10</B>&nbsp;&nbsp;Add an <CODE>undo</CODE> command, which causes the effect of the previous command
+to be nullified.  That is, if the previous command was a cell selection
+command, <CODE>undo</CODE> will return to the previously selected cell.  If the
+previous command was a <CODE>put</CODE>, <CODE>undo</CODE> will re-<CODE>put</CODE> the previous
+expressions in every affected cell.  You don't need to undo <CODE>load</CODE> or
+<CODE>exit</CODE> commands.  To do this, you'll need to modify the way the other
+commands work.
+
+
+<P>
+<B>25.11</B>&nbsp;&nbsp;Add an <CODE>accumulate</CODE> procedure that can be used as a function in
+formulas.  Instead of specifying a sequence of cells explicitly, in a
+formula like
+
+<P><PRE>(put (+ c2 c3 c4 c5 c6 c7) c10)
+</PRE>
+
+<P>we want to be able to say
+
+<P><PRE>(put (accumulate + c2 c7) c10)
+</PRE>
+
+<P>In general, the two cell names should be taken as corners of a
+rectangle, all of whose cells should be included, so these two commands
+are equivalent:
+
+<P><PRE>(put (accumulate * a3 c5) d7)
+(put (* a3 b3 c3 a4 b4 c4 a5 b5 c5) d7)
+</PRE>
+
+<P>Modify <CODE>pin-down</CODE> to convert the <CODE>accumulate</CODE> form into
+the corresponding spelled-out form.
+
+
+<P>
+<B>25.12</B>&nbsp;&nbsp;Add variable-width columns to the spreadsheet.  There should be a command to
+set the print width of a column.  This may mean that the spreadsheet can
+display more or fewer than six columns.
+
+
+<P>
+
+<HR>
+<A NAME="ft1" HREF="spread-implement#text1">[1]</A> The vector elements are numbered from zero, but we number rows and
+columns from one, subtracting one in the selector that actually extracts
+information from the array of cells.<P>
+<A NAME="ft2" HREF="spread-implement#text2">[2]</A> Not every version of Scheme has
+this behavior.  If you find that you have to hit <CODE>return</CODE> twice after exhibiting
+the value of a formula, take out one of the <CODE>read-line</CODE> invocations.<P>
+<A NAME="ft3" HREF="spread-implement#text3">[3]</A> We originally wrote two separate helper procedures for the two
+cases, like this:
+
+<P><PRE>(define (put-all-cells-in-row formula row)
+  (row-helper formula 1 26 row))
+
+(define (row-helper formula this-col max-col row)
+  (if (&gt; this-col max-col)
+      'done
+      (begin (try-putting formula <B>(make-id this-col row)</B>)
+	     (row-helper formula (+ this-col 1) max-col row))))
+
+(define (put-all-cells-in-col formula col)
+  (column-helper formula 1 30 col))
+
+(define (column-helper formula this-row max-row col)
+  (if (&gt; this-row max-row)
+      'done
+      (begin (try-putting formula <B>(make-id col this-row)</B>)
+	     (column-helper formula (+ this-row 1) max-row col))))
+</PRE>
+
+<P>but the procedures were so similar that we decided to generalize the
+pattern.<P>
+<A NAME="ft4" HREF="spread-implement#text4">[4]</A> In fact, <CODE>process-command</CODE>
+also invokes <CODE>pin-down</CODE> when the user types a formula in place of a
+command.  In that situation, the result doesn't go into a cell but is
+immediately evaluated and printed.<P>
+<A NAME="ft5" HREF="spread-implement#text5">[5]</A> You can
+think of the <CODE>cell</CODE> notation in generalized formulas as a kind of
+special form, but <CODE>pin-down</CODE> has turned those into specific cell IDs
+before the formula is eligible for evaluation as an expression.<P>
+<P><A HREF="../ss-toc2.html">(back to Table of Contents)</A><P>
+<A HREF="../ssch24/spread.html"><STRONG>BACK</STRONG></A>
+chapter thread <A HREF="database.html"><STRONG>NEXT</STRONG></A>
+
+<P>
+<ADDRESS>
+<A HREF="../index.html">Brian Harvey</A>, 
+<CODE>bh@cs.berkeley.edu</CODE>
+</ADDRESS>
+</BODY>
+</HTML>