about summary refs log tree commit diff stats
path: root/js/games/nluqo.github.io/~bh/ssch21/functions-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/ssch21/functions-implement
parent5d012c6c011a9dedf7d0a098e456206244eb5a0f (diff)
downloadtour-562a9a52d599d9a05f871404050968a5fd282640.tar.gz
*
Diffstat (limited to 'js/games/nluqo.github.io/~bh/ssch21/functions-implement')
-rw-r--r--js/games/nluqo.github.io/~bh/ssch21/functions-implement1026
1 files changed, 1026 insertions, 0 deletions
diff --git a/js/games/nluqo.github.io/~bh/ssch21/functions-implement b/js/games/nluqo.github.io/~bh/ssch21/functions-implement
new file mode 100644
index 0000000..0661202
--- /dev/null
+++ b/js/games/nluqo.github.io/~bh/ssch21/functions-implement
@@ -0,0 +1,1026 @@
+\input bkmacs
+\pagetag{\oz}
+\photo{Once you see how it works, it's not so mysterious.}{\pspicture{4in}{oz}{oz}{}}
+
+\let\ttfont=\twelvett
+\chapter{Example: The\/ {\ttfont Functions} Program}
+\chaptag{\fimp}
+\normaltext
+
+In Chapter \functions\ you used a program called \ttidx{functions} to explore
+some of Scheme's primitive functions.  Now we're going to go back to that
+program from the other point of view: instead of using the program to learn
+about functions, we're going to look at how the program works as an example
+of programming with input and output.
+
+\backskipsubhd{The Main Loop}{7}
+
+The {\tt functions} program is an infinite loop similar to Scheme's
+\idx{read-eval-print loop}.  It reads in a function name and some arguments,
+prints the result of applying that function to those arguments, and then
+does the whole thing over again.
+
+There are some complexities, though.  The {\tt functions} program keeps
+asking you for arguments until it has enough.  This means that the {\tt
+read} portion of the loop has to read a function name, figure out how many
+arguments that procedure takes, and then ask for the right number of
+arguments.  On the other hand, each argument is an implicitly quoted datum
+rather than an expression to be evaluated; the {\tt functions} evaluator
+avoids the recursive complexity of arbitrary subexpressions within
+expressions.  (That's why we wrote it:\ to focus attention on one function
+invocation at a time, rather than on the composition of functions.)
+Here's the main loop:
+
+{\prgex%
+(define (\ufun{functions-loop})
+  (let ((fn-name (get-fn)))
+    (if (equal? fn-name 'exit)
+	"Thanks for using FUNCTIONS!"
+	(let ((args (get-args (arg-count fn-name))))
+	  (if (not (in-domain? args fn-name))
+	      (show "Argument(s) not in domain.")
+	      (show-answer (apply (scheme-procedure fn-name) args)))
+	  (functions-loop)))))
+}
+
+\noindent This invokes a lot of helper procedures.  {\tt Arg-count} takes
+the name of a procedure as its argument and returns the number of arguments
+that the given procedure takes.  {\tt In-domain?} takes a list and the name
+of a procedure as arguments; it returns {\tt \#t} if the elements of the list
+are valid arguments to the given procedure.  {\tt Scheme-procedure} takes a
+name as its argument and returns the Scheme procedure with the given name.
+We'll get back to these helpers later.
+
+The other helper procedures are the ones that do the input and output.  The
+actual versions are more complicated because of error checking; we'll show
+them to you later.
+
+{\prgex%
+(define (get-fn)                             ;; first version
+  (display "Function: ")
+  (read))
+
+(define (\ufun{get-args} n)
+  (if (= n 0)
+      '()
+      (let ((first (get-arg)))
+	(cons first (get-args (- n 1))))))
+
+(define (get-arg)                            ;; first version
+  (display "Argument: ")
+  (read))
+
+(define (\ufun{show-answer} answer)
+  (newline)
+  (display "The result is: ")
+  (if (not answer)
+      (show "#F")
+      (show answer))
+  (newline))
+}
+
+\noindent (That weird {\tt if} expression in {\tt show-answer} is needed
+because in some old versions of Scheme the empty list means the same as {\tt
+\#f}.  We wanted to avoid raising this issue in Chapter \functions, so we
+just made sure that false values always printed as {\tt \#F}.)
+
+\subhd{The Difference between a Procedure and Its Name}
+
+You may be wondering why we didn't just say
+
+{\prgex%
+(show-answer (apply fn-name args))
+}
+
+\noindent in the definition of {\tt functions-loop}.  Remember that the value of
+the variable {\tt fn-name} comes from {\tt get-fn}, which invokes {\tt read}.
+Suppose you said
+
+{\medskipamount=3pt\prgexskipamount=10pt
+
+{\prgex%
+(define x (read))
+}
+
+\noindent and then typed
+
+{\prgex%
+(+ 2 3)
+}
+
+\noindent The value of {\tt x} would be the three element list {\tt
+(+~2~3)}, not the number five.
+
+Similarly, if you type ``butfirst,'' then read will return the {\it
+word\/} {\tt butfirst}, not the procedure of that name.  So we need a way to
+turn the name of a function into the procedure itself.
+
+\backskipsubhd{The Association List of Functions}{4}
+
+We accomplish this by creating a huge association list that contains
+all of the functions the program knows about.  Given a word, such as
+{\tt butfirst}, we need to know three things:
+
+\medskip
+{\parindent=1em\parskip=8pt
+\bb The Scheme procedure with that name (in this case, the {\tt butfirst}
+procedure).
+\bb The number of arguments required by the given procedure
+(one).\footnt{Some Scheme procedures can accept any number of arguments,
+but for the purposes of the {\tt functions} program we restrict these
+procedures to their most usual case, such as two arguments for~{\tt +}.}
+\bb The types of arguments required by the given procedure (one word or
+sentence, which must not be empty).
+
+}\medskip
+
+We need to know the number of arguments the procedure requires because the
+program prompts the user individually for each argument; it has to know how
+many to ask for.  Also, it needs to know the domain of each function so
+it can complain if the arguments you give it are not in the
+domain.\footnt{Scheme would complain all by itself, of course, but would
+then stop running the {\tt functions} program.  We want to catch the error
+before Scheme does, so that after seeing the error message you're still in
+{\tt functions}.  As we mentioned in Chapter \implhof, a program meant for
+beginners, such as the readers of Chapter \functions, should be
+especially robust.}
+
+This means that each entry in the association list is a list of four
+elements:
+% .  It looks like this:
+
+{\prgex%
+(define *the-functions*                      ;; partial listing
+  (list (list '* * 2 (lambda (x y) (and (number? x) (number? y))))
+	(list '+ + 2 (lambda (x y) (and (number? x) (number? y))))
+	(list 'and (lambda (x y) (and x y)) 2
+	      (lambda (x y) (and (boolean? x) (boolean? y))))
+	(list 'equal? equal? 2 (lambda (x y) #t))
+	(list 'even? even? 1 integer?)
+	(list 'word word 2 (lambda (x y) (and (word? x) (word? y))))))
+}
+
+\noindent The real list is much longer, of course, but you get the
+idea.\footnt{Since {\tt and} is a special form, we can't just say
+
+{\prgex%
+(list 'and and 2 (lambda (x y) (and (boolean? x) (boolean? y))))
+}
+
+That's because special forms can't be elements of lists.  Instead, we have
+to create a normal procedure that can be put in a list but computes the same
+function as {\tt and}:
+
+{\prgex%
+(lambda (x y) (and x y))
+}
+
+\noindent We can get away with this because in the {\tt functions} program we
+don't care about argument evaluation, so it doesn't matter that {\tt and} is
+a special form.  We do the same thing for {\tt if} and {\tt or}.} It's a
+convention in Scheme programming that names of global variables used
+throughout a program are surrounded by {\tt *}asterisks{\tt *} to
+distinguish them from parameters of procedures.
+
+Here are the selector procedures for looking up information in this a-list:
+
+{\prgex%
+(define (\ufun{scheme-procedure} fn-name)
+  (cadr (assoc fn-name *the-functions*)))
+
+(define (\ufun{arg-count} fn-name)
+  (caddr (assoc fn-name *the-functions*)))
+
+(define (\ufun{type-predicate} fn-name)
+  (cadddr (assoc fn-name *the-functions*)))
+}
+
+\subhd{Domain Checking}
+
+} % skip kludge
+
+Note that we represent the domain of a procedure by another
+procedure.\footnt{The domain of a procedure is a set, and sets are generally
+represented in programs as lists.  You might think that we'd have to store,
+for example, a list of all the legal arguments to {\tt butfirst}.  But that
+would be impossible, since that list would have to be infinitely large.
+Instead, we can take advantage of the fact that the only use we make of this
+set is membership testing, that is, finding out whether a particular argument
+is in a function's domain.} Each domain-checking procedure, or {\it type
+predicate,\/} takes the same arguments as the procedure whose domain it
+checks.  For example, the type predicate for {\tt +} is
+
+{\prgex%
+(lambda (x y) (and (number? x) (number? y)))
+}
+
+\noindent The type predicate returns {\tt \#t} if its arguments
+are valid and {\tt \#f} otherwise.  So in the case of {\tt +}, any two
+numbers are valid inputs, but any other types of arguments aren't.
+
+Here's the {\tt in-domain?} predicate:
+
+{\prgex%
+(define (\ufun{in-domain?} args fn-name)
+  (apply (type-predicate fn-name) args))
+}
+
+Of course, certain type predicates are applicable to more than one
+procedure.  It would be silly to type
+
+{\prgex%
+(lambda (x y) (and (number? x) (number? y)))
+}
+
+\noindent for {\tt +}, {\tt -}, {\tt =}, and so on.  Instead, we give this
+function a name:
+
+{\prgex%
+(define (\ufun{two-numbers?} x y)
+  (and (number? x) (number? y)))
+}
+
+\noindent We then refer to the type predicate by name in the a-list:
+
+{\prgex%
+(define *the-functions*                     ;; partial listing, revised
+  (list (list '* * 2 two-numbers?)
+	(list '+ + 2 two-numbers?)
+	(list 'and (lambda (x y) (and x y)) 2
+	      (lambda (x y) (and (boolean? x) (boolean? y))))
+	(list 'equal? equal? 2 (lambda (x y) #t))
+	(list 'even? even? 1 integer?)
+	(list 'word word 2 (lambda (x y) (and (word? x) (word? y))))))
+}
+
+Some of the type predicates are more complicated.  For example,
+here's the one for the {\tt member?} and {\tt appearances} functions:
+
+{\prgex%
+(define (\ufun{member-types-ok?} small big)
+  (and (word? small)
+       (or (sentence? big) (and (word? big) (= (count small) 1)))))
+}
+
+\noindent {\tt Item} also has a complicated domain:
+
+{\prgex%
+(lambda (n stuff)
+  (and (integer? n) (> n 0)
+       (word-or-sent? stuff) (<= n (count stuff))))
+}
+
+\noindent This invokes {\tt word-or-sent?}, which is itself the type
+predicate for the {\tt count} procedure:
+
+{\prgex%
+(define (word-or-sent? x)
+  (or (word? x) (sentence? x)))
+}
+
+On the other hand, some are less complicated.  {\tt Equal?} will accept any
+two arguments, so its type predicate is just
+
+{\prgex%
+(lambda (x y) #t)
+}
+
+The complete listing at the end of the chapter shows the details of all these
+procedures.  Note that the {\tt functions} program has a more restricted
+idea of domain than Scheme does.  For example, in Scheme
+
+{\prgex%
+(and 6 #t)
+}
+
+\noindent returns {\tt \#t} and does not generate an error.  But in
+the {\tt functions} program the argument {\tt 6} is considered out of the
+domain.\footnt{Why did we choose to restrict the domain?  We were trying
+to make the point that invoking a procedure makes sense only with appropriate
+arguments; that point is obscured by the complicating fact that Scheme
+interprets any non-{\tt \#f} value as true.  In the {\tt functions} program,
+where composition of functions is not allowed, there's no benefit to Scheme's
+more permissive rule.}
+
+If you don't like math, just ignore the domain predicates for the
+mathematical primitives; they involve facts about the domains of math
+functions that we don't expect you to know.\footnt{A reason that we
+restricted the domains of some mathematical functions is to protect
+ourselves from the fact that some version of Scheme support complex numbers
+while others do not.  We wanted to write one version of {\tt functions} that
+would work in either case; sometimes the easiest way to avoid possible
+problems was to restrict some function's domain.}
+
+\subhd{Intentionally Confusing a Function with Its Name}
+
+{\medskipamount=5pt\prgexskipamount=10pt
+
+Earlier we made a big deal about the difference between a procedure and its
+name, to make sure you wouldn't think you can apply the {\it word\/} {\tt
+butfirst} to arguments.  But the {\tt functions} program completely hides
+this distinction from the user:
+
+{\prgex%
+Function: count
+Argument: butlast
+
+The result is: 7
+
+Function: every
+Argument: butlast
+Argument: (helter skelter)
+
+The result is: (HELTE SKELTE)
+}
+
+When we give {\tt butlast} as an argument to {\tt count}, it's as if we'd said
+
+{\prgex%
+(count 'butlast)
+}
+
+\noindent In other words, it's taken as a word.  But when we give {\tt
+butlast} as an argument to {\tt every}, it's as if we'd said
+
+{\prgex%
+(every butlast '(helter skelter))
+}
+
+How can we treat some arguments as quoted and others not?  The way this
+works is that {\it everything\/} is considered a word or a sentence by the
+{\tt functions} program.  The higher-order functions {\tt every} and {\tt
+keep} are actually represented in the {\tt functions} implementation by
+Scheme procedures that take the {\it name\/} of a function as an argument,
+instead of a procedure itself as the ordinary versions do:
+
+{\prgex%
+(define (\ufun{named-every} fn-name list)
+  (every (scheme-procedure fn-name) list))
+
+(define (\ufun{named-keep} fn-name list)
+  (keep (scheme-procedure fn-name) list))
+
+> (every first '(another girl))
+(A G)
+> (named-every 'first '(another girl))
+(A G)
+> (every 'first '(another girl))
+ERROR: ATTEMPT TO APPLY NON-PROCEDURE FIRST
+}
+
+} % end skip kludge
+
+\noindent This illustration hides a subtle point.  When we invoked {\tt
+named-every} at a Scheme prompt, we had to quote the word {\tt first}
+that we used as its argument.  But when you run the {\tt functions} program,
+you don't quote anything.  The point is that {\tt functions} provides an
+evaluator that uses a different notation from Scheme's notation.  It may be
+clearer if we show an interaction with an imaginary version of {\tt
+functions} that {\it does\/} use Scheme notation:
+
+{\prgex%
+Function: first
+Non-Automatically-Quoted-Argument: 'datum
+
+The result is: D
+
+Function: first
+Non-Automatically-Quoted-Argument: datum
+
+ERROR: THE VARIABLE DATUM IS UNBOUND.
+}
+
+\noindent We didn't want to raise the issue of quoting at that early point
+in the book, so we wrote {\tt functions} so that {\it every\/} argument
+is automatically quoted.  Well, if that's the case, it's true even when
+we're invoking {\tt every}.  If you say
+
+{\prgex%
+Function: every
+Argument: first
+\ellipsis
+}
+
+\noindent then by the rules of the {\tt functions} program, that argument is
+the quoted word {\tt first}.  So {\tt named-every}, the procedure that
+pretends to be {\tt every} in the {\tt functions} world, has to ``un-quote''
+that argument by looking up the corresponding procedure.
+
+\subhd{More on Higher-Order Functions}
+
+One of the higher-order functions that you can use in the {\tt functions}
+program is called {\tt number-of-arguments}.  It takes a procedure (actually
+the name of a procedure, as we've just been saying) as argument and returns
+the number of arguments that that procedure accepts.  This example is
+unusual because there's no such function in Scheme.  (It would be
+complicated to define, for one thing, because some Scheme procedures can
+accept a variable number of arguments.  What should {\tt
+number-of-arguments} return for such a procedure?)
+
+The implementation of {\tt number-of-arguments} makes use of the same a-list
+of functions that the {\tt functions} evaluator itself uses.  Since the
+{\tt functions} program needs to know the number of arguments for every
+procedure anyway, it's hardly any extra effort to make that information
+available to the user.  We just add an entry to the a-list:
+
+{\prgex%
+(list '\ufun{number-of-arguments} arg-count 1 valid-fn-name?)
+}
+
+\noindent The type predicate merely has to check that the argument
+is found in the a-list of functions:
+
+{\prgex%
+(define (\ufun{valid-fn-name?} name)
+  (assoc name *the-functions*))
+}
+
+The type checking for the arguments to {\tt every} and {\tt keep} is
+unusually complicated because what's allowed as the second argument (the
+collection of data) depends on which function is used as the first argument.
+For example, it's illegal to compute
+
+{\prgex%
+(every square '(think for yourself))
+}
+
+\noindent even though either of those two arguments would be allowable if we
+changed the other one:
+
+{\prgex%
+> (every square '(3 4 5))
+(9 16 25)
+
+> (every first '(think for yourself))
+(T F Y)
+}
+
+The type-checking procedures for {\tt every} and {\tt keep} use a common
+subprocedure.  The one for {\tt every} is
+
+{\prgex%
+(lambda (fn stuff)
+  (hof-types-ok? fn stuff word-or-sent?))
+}
+
+\noindent and the one for {\tt keep} is
+
+{\prgex%
+(lambda (fn stuff)
+  (hof-types-ok? fn stuff boolean?))
+}
+
+\noindent The third argument specifies what types of results {\tt fn} must
+return when applied to the elements of {\tt stuff}.
+
+{\prgex%
+(define (hof-types-ok? fn-name stuff range-predicate)
+  (and (valid-fn-name? fn-name)
+       (= 1 (arg-count fn-name))
+       (word-or-sent? stuff)
+       (empty? (keep (lambda (element)
+		       (not ((type-predicate fn-name) element)))
+		     stuff))
+       (null? (filter (lambda (element)
+			(not (range-predicate element)))
+		      (map (scheme-procedure fn-name)
+			   (every (lambda (x) x) stuff))))))
+}
+
+\noindent This says that the function being used as the first argument must
+be a one-argument function (so you can't say, for example, {\tt every} of
+{\tt word} and something); also, {\it each element\/} of the second argument
+must be an acceptable argument to that function.  (If you {\tt keep} the
+unacceptable arguments, you get nothing.)  Finally, each invocation of the
+given function on an element of {\tt stuff} must return an object of the
+appropriate type:\ words or sentences for {\tt every}, true or false for
+{\tt keep}.\footnt{That last argument to {\tt and} is complicated.  The
+reason we use {\tt map} instead of {\tt every} is that the results of the
+invocations of {\tt fn} might not be words or sentences, so {\tt every}
+wouldn't accept them.  But {\tt map} has its own limitation:  It won't
+accept a word as the {\tt stuff} argument.  So we use {\tt every} to turn
+{\tt stuff} into a sentence---which, as you know, is really a list---and
+that's guaranteed to be acceptable to {\tt map}.  (This is an example of a
+situation in which respecting a data abstraction would be too horrible to
+contemplate.)}
+
+\subhd{More Robustness}
+
+The program we've shown you so far works fine, as long as the user
+never makes a mistake.  Because this program was written for absolute
+novices, we wanted to bend over backward to catch any kind of strange input
+they might give us.
+
+Using {\tt read} to accept user input has a number of disadvantages:
+
+\medskip
+{\parindent=1em
+\bb If the user enters an empty line, {\tt read} continues waiting silently
+for input.
+\bb If the user types an unmatched open parenthesis, {\tt read} continues
+reading forever.
+\bb If the user types two expressions on a line, the second one will be
+taken as a response to the question the {\tt functions} program hasn't asked
+yet.
+
+}\medskip
+
+\noindent We solve all these problems by using {\tt read-line} to read
+exactly one line, even if it's empty or ill-formed, and then checking
+explicitly for possible errors.
+
+{\tt Read-line} treats parentheses no differently from any other character.
+That's an advantage if the user enters mismatched or inappropriately nested
+parentheses.  However, if the user correctly enters a sentence as an
+argument to some function, {\tt read-line} will include the initial open
+parenthesis as the first character of the first word, and the final close
+parenthesis as the last character of the last word.  {\tt Get-arg} must
+correct for these extra characters.
+
+Similarly, {\tt read-line} treats number signs ({\tt #}) like any other
+character, so it doesn't recognize {\tt #t} and {\tt #f} as special values.
+Instead it reads them as the strings {\tt "#t"} and {\tt "#f"}.  {\tt
+Get-arg} calls {\tt booleanize} to convert those strings into Boolean values.
+
+{\prgex%
+(define (\ufun{get-arg})
+  (display "Argument: ")
+  (let ((line (read-line)))
+    (cond ((empty? line)
+	   (show "Please type an argument!")
+	   (get-arg))
+	  ((and (equal? "(" (first (first line)))
+		(equal? ")" (last (last line))))
+	   (let ((sent (remove-first-paren (remove-last-paren line))))
+	     (if (any-parens? sent)
+		 (begin (show "Sentences can't have parentheses inside.")
+			(get-arg))
+		 (map booleanize sent))))
+	  ((any-parens? line)
+	   (show "Bad parentheses")
+	   (get-arg))
+	  ((empty? (bf line)) (booleanize (first line)))
+	  (else (show "You typed more than one argument!  Try again.")
+		(get-arg)))))
+}
+
+\hyphenchar\tentt=\count123
+\noindent {\tt Get-arg} invokes {\tt any-parens?}, {\tt
+remove-first-paren}, {\tt remove-last-paren}, and {\tt booleanize}, whose
+meanings should be obvious from their names.  You can look up their
+definitions in the complete listing at the end of this chapter.
+
+\hyphenchar\tentt=-1
+{\tt Get-fn} is simpler than {\tt get-arg}, because there's no issue about
+parentheses, but it's still much more complicated than the original version,
+because of error checking.
+
+{\medskipamount=5pt
+
+{\prgex%
+(define (\ufun{get-fn})
+  (display "Function: ")
+  (let ((line (read-line)))
+    (cond ((empty? line)
+	   (show "Please type a function!")
+	   (get-fn))
+	  ((not (= (count line) 1))
+	   (show "You typed more than one thing!  Try again.")
+	   (get-fn))
+	  ((not (valid-fn-name? (first line)))
+	   (show "Sorry, that's not a function.")
+	   (get-fn))
+	  (else (first line)))))
+}
+
+\noindent This version of {\tt get-fn} uses {\tt valid-fn-name?} (which
+you've already seen) to ensure that what the user types is the name of a
+function we know about.
+
+There's a problem with using {\tt read-line}.  As we mentioned in a pitfall
+in Chapter \io, reading some input with {\tt read} and then reading the next
+input with {\tt read-line} results in {\tt read-line} returning an empty
+line left over by {\tt read}.  Although the {\tt functions} program doesn't
+use {\tt read}, Scheme itself used {\tt read} to read the {\tt (functions)}
+expression that started the program.  Therefore,
+{\tt get-fn}'s first attempt to read a function name will see an empty
+line.  To fix this problem, the {\tt functions} procedure has an
+extra invocation of {\tt read-line}:
+
+{\prgex%
+(define (functions)
+  (read-line)
+  (show "Welcome to the FUNCTIONS program.")
+  (functions-loop))
+}
+
+} % skip kludge
+
+\subhd{Complete Program Listing}
+
+\bigskip
+\listing{functions.scm}
+
+\esubhd{Exercises}
+
+{\medskipamount=4pt
+{\exercise
+The {\tt get-args} procedure has a {\tt let} that creates the variable {\tt
+first}, and then that variable is used only once inside the body of the {\tt
+let}.  Why doesn't it just say the following?
+
+{\prgex%
+(define (get-args n)
+  (if (= n 0)
+      '()
+      (cons (get-arg) (get-args (- n 1)))))
+}
+}
+
+\solution
+
+The {\tt let} is needed to ensure that the
+arguments in the returned list are in the correct order:
+
+Suppose that the user has chosen a two-argument function, so
+{\tt functions-loop} calls {\tt get-args} with the argument 2.
+{\tt Get-args} will ask
+the user for two arguments; suppose that the user types ``the''
+as the first response and ``beatles'' as the second response.  So {\tt
+get-args} {\it should\/} return the list {\tt (the~beatles)}.
+
+Let's trace what happens, using the little people model.  Glenda the {\tt
+get-args} specialist is hired, with {\tt n} being 2.  Since her {\tt n}
+isn't 0, she evaluates the {\tt cons} expression.
+
+Assume that in this version of Scheme the arguments to a
+procedure are evaluated from right to left.  The first thing that happens
+is that Glenda hires Gerry, another {\tt get-args} specialist, giving him
+an argument of 1.  (Later, Glenda will hire a {\tt get-arg} specialist, and
+then a {\tt cons} specialist.)
+
+Gerry's job is to get one argument.  We'll
+take a leap of faith and assume that he correctly reads the first expression
+the user typed, namely, the word {\tt the}.  So Gerry returns the list {\tt
+(the)}.
+
+Now Glenda hires a {\tt get-arg} specialist, who reads {\tt beatles} from
+the user.  Glenda then hires a {\tt cons} specialist to insert {\tt beatles}
+onto the front of {\tt (the)}, giving {\tt (beatles~the)} as her final
+answer.  But this is wrong!  Glenda's list is backwards; instead of
+returning {\tt (the~beatles)} she returned {\tt (beatles~the)}.
+
+The {\tt let} in the correct version of {\tt get-args} is to ensure that the
+call to {\tt read} happens {\it before\/} the recursive call, thus putting
+the elements into the list in the right order.
+
+@
+
+{\exercise
+The domain-checking function for {\tt equal?} is
+
+{\prgex%
+(lambda (x y) #t)
+}
+
+\noindent This seems silly; it's a function of two arguments that ignores
+both arguments and always returns {\tt \#t}.  Since we know ahead of time
+that the answer is {\tt \#t}, why won't it work to have {\tt equal?}'s entry
+in the a-list be
+
+{\prgex%
+(list 'equal? equal? 2 #t)
+}
+}
+\solution
+The {\tt functions} program uses the type-checking predicate by
+invoking it with the given arguments.  If the entry were {\tt #t}
+instead of a function, then invoking it would be an error.
+The relevant part of the program is {\tt in-domain?}:
+
+{\prgex%
+(define (in-domain? args fn-name)
+  (apply (type-predicate fn-name) args))
+}
+
+\noindent If an entry had {\tt #t} as the type ``predicate,'' then
+this would be equivalent to
+
+{\prgex%
+(apply #t args)
+}
+@
+} %%% kludge
+
+{\exercise
+Every time we want to know something about a function that the user typed
+in, such as its number of arguments or its domain-checking predicate, we
+have to do an {\tt assoc} in {\tt *the-functions*}.  That's inefficient.
+Instead, rewrite the program so that {\tt get-fn} returns a function's entry
+from the a-list, instead of just its name.  Then rename the variable {\tt
+fn-name} to {\tt fn-entry} in the {\tt functions-loop} procedure, and rewrite
+the selectors {\tt scheme-procedure}, {\tt arg-count}, and so on, so that
+they don't invoke {\tt assoc}.
+}
+
+\solution
+The changed parts of the program are shown here in boldface.
+
+{\prgex%
+(define (get-fn)
+  (display "Function: ")
+  (let ((line (read-line)))
+    (cond ((empty? line)
+	   (show "Please type a function!")
+	   (get-fn))
+	  ((not (= (count line) 1))
+	   (show "You typed more than one thing!  Try again.")
+	   (get-fn))
+	  ((not (valid-fn-name? (first line)))
+	   (show "Sorry, that's not a function.")
+	   (get-fn))
+	  (else \pmb{(assoc} (first line) \pmb{*the-functions)}))))
+
+(define (functions-loop)
+  (let ((\pmb{fn-entry} (get-fn)))
+    (if (equal? \pmb{(function-name fn-entry)} 'exit)
+	"Thanks for using FUNCTIONS!"
+	(let ((args (get-args (arg-count \pmb{fn-entry}))))
+	  (if (not (in-domain? args \pmb{fn-entry}))
+	     (show "Argument(s) not in domain.")
+	     (show-answer (apply (scheme-function \pmb{fn-entry}) args)))
+	  (functions-loop)))))
+
+(define (function-name \pmb{fn-entry})
+  (car \pmb{fn-entry}))
+
+(define (scheme-function \pmb{fn-entry})
+  (cadr \pmb{fn-entry}))
+
+(define (arg-count \pmb{fn-entry})
+  (caddr \pmb{fn-entry}))
+
+(define (type-predicate \pmb{fn-entry})
+  (cadddr \pmb{fn-entry}))
+}
+
+A new selector {\tt function-name} was needed to look for the use
+of the word {\tt exit} in place of a function name.
+@
+
+{\exercise
+Currently, the program always gives the message ``argument(s) not in
+domain'' when you try to apply a function to bad arguments.  Modify the
+program so that each record in {\tt *the-functions*} also contains a
+specific out-of-domain message like ``both arguments must be numbers,'' then
+modify {\tt functions} to look up and print this error message along with
+``argument(s) not in domain.''
+}
+
+\solution
+First we invent a new selector for the error message:
+
+{\prgex%
+(define (domain-error-message fn-name)
+  (car (cddddr (assoc fn-name *the-functions*))))
+}
+
+\noindent We can't just say {\tt caddddr} because those functions
+are only defined for up to four steps.  For elements this far into
+the list, it might be clearer to use {\tt list-ref}:
+
+{\prgex%
+(define (domain-error-message fn-name)
+  (list-ref (assoc fn-name *the-functions*) 4))
+}
+
+Now we use the more specific error message.  
+The changed parts of the procedure are shown here in boldface.
+
+{\prgex%
+(define (functions-loop)
+  (let ((fn-name (get-fn)))
+    (if (equal? fn-name 'exit)
+	"Thanks for using FUNCTIONS!"
+	(let ((args (get-args (arg-count fn-name))))
+	  (if (not (in-domain? args fn-name))
+	      \pmb{(begin} (show "Argument(s) not in domain:")
+		     \pmb{(show (domain-error-message fn-name)))}
+	      (show-answer (apply (scheme-function fn-name) args)))
+	  (functions-loop)))))
+}
+
+Of course we also must update the function list itself:
+
+{\prgex%
+(define *the-functions*
+  (list (list '* * 2 two-numbers? "Both arguments must be numbers.")
+	\ellipsis
+	(list 'butfirst butfirst 1 not-empty?
+	      "The argument must be a non-empty word or sentence.")
+	\ellipsis))
+}
+@
+
+\vfill\eject
+
+{\exercise
+Modify the program so that it prompts for the arguments this way:
+
+{\prgex%
+Function: if
+First Argument: #t
+Second Argument: paperback
+Third Argument: writer
+
+The result is: PAPERBACK
+}
+
+\noindent but if there's only one argument, the program shouldn't say {\tt
+First}:
+
+{\prgex%
+Function: sqrt
+Argument: 36
+
+The result is 6
+}}
+
+\solution 
+
+The easiest way is not to modify {\tt get-arg} at all:
+
+{\prgex%
+(define (get-args n)
+  (if (= n 1)
+      (list (get-arg))
+      (get-args-helper 1 n)))
+
+(define (get-args-helper this total)
+  (if (> this total)
+      '()
+      (begin (display (ordinal this))
+	     (display " ")
+	     (let ((first (get-arg)))
+	       (cons first (get-args-helper (+ this 1) total))))))
+
+(define (ordinal n)
+  (item n '("First" "Second" "Third" "Fourth" "Fifth")))
+}
+
+But it's a little bit of a kludge that {\tt get-args} prints half the prompt
+and {\tt get-arg} prints the other half.  Instead, we can have {\tt get-args}
+figure out what the argument prompt should be, and tell {\tt get-arg} what
+to print:
+
+{\prgex%
+(define (get-args n)
+  (if (= n 1)
+      (list (get-arg "Argument: "))
+      (get-args-helper 1 n)))
+
+(define (get-args-helper this total)
+  (if (> this total)
+      '()
+      (let ((first (get-arg (word (ordinal this) " Argument: "))))
+	(cons first (get-args-helper (+ this 1) total)))))
+
+(define (get-arg prompt)
+  (display prompt)
+  ;;{\rm everything else get-arg used to do$\ldots$}
+  )
+}
+@
+
+{\exercise
+The {\tt assoc} procedure might return {\tt \#f} instead of an a-list
+record.  How come it's okay for {\tt arg-count} to take the {\tt caddr} of
+{\tt assoc}'s return value if {\tt (caddr~\#f)} is an error?
+}
+
+\solution
+{\tt Get-fn} invokes {\tt valid-fn-name?} to check whether or not
+the requested function is in the a-list.  If not, {\tt get-fn} asks
+for another function name, so the selectors like {\tt arg-count}
+will never be invoked with this nonexistent name.
+@
+
+{\exercise
+Why is the domain-checking predicate for the {\tt word?} function
+
+{\prgex%
+(lambda (x) #t)
+}
+
+\noindent instead of the following procedure?
+
+{\prgex%
+(lambda (x) (word? x))
+}
+}
+
+\solution
+The domain of the {\tt word?} function is {\it anything,} not just words.
+Invoking the {\tt word?} predicate is like asking the question ``is this a
+word?'' You can ask that question about anything you like:  ``Is {\tt
+(1~2~3)} a word?'' --- ``No.'' ``Is {\tt \#t} a word?'' --- ``No.'' ``Is
+{\tt banana} a word?'' --- ``Yes.'' So {\it everything\/} is in the domain
+of {\tt word?}, and its domain-checking predicate always returns {\tt #t}.
+
+Not every predicate has a domain that includes every possible value.
+For example, the domain of the predicate {\tt even?} is just numbers;
+you can't ask Scheme the question ``is {\tt banana} even?''  Therefore,
+not every predicate will have
+
+{\prgex%
+(lambda (x) #t)
+}
+
+\noindent as its domain-checking function.  But even for {\tt even?}, it
+would be silly to use {\tt even?} itself as the domain-checking function.
+If the domain of a predicate included only those things for which the
+predicate is true, then that predicate could never return {\tt #f}!  That
+would make it a rather useless predicate.  There's a difference between
+asking Scheme a question to which it replies ``no'' and asking it a
+meaningless question.
+
+% Many people have trouble with the difference between getting back the answer
+% ``no'' to a question and asking a meaningless question.  If I say ``Is 5
+% even?'' the answer is no.  But if I ask ``Is {\tt elephant} even?'' it's a
+% meaningless question.  The domain of the {\tt even?} primitive is just
+% numbers.  On the other hand, the question ``Is such-and-such a word?'' is
+% never meaningless, no matter what object I'm asking the question about.
+
+@
+
+{\exercise
+What is the value of the following Scheme expression?
+
+{\prgex%
+(functions)
+}
+}
+
+
+\solution
+
+Invoking the expression {\tt (functions)} causes a lot of activity to occur,
+but the value of that expression is always the string {\tt "Thanks for using
+FUNCTIONS!"}
+
+The {\tt functions} procedure has two (or three) expressions in its body, so
+Scheme evaluates them all and returns the value of the last one.  In this
+case, the last one is a call to {\tt functions-loop}, so whatever {\tt
+functions-loop} returns, {\tt functions} will return that, too.
+
+{\tt Functions-loop} is recursive.  In the recursive case, there's a {\tt
+let} with two expressions, so Scheme evaluates them both and returns the
+value of the second one.  The second expression is a recursive call to {\tt
+functions-loop}, so whatever the recursive call returns is what the
+outer call will also return.  Thus, the string {\tt "Thanks for using
+FUNCTIONS!"}, returned by the base case, will be the return value from every
+single call to {\tt functions-loop}, including the first one, so it will
+also be the return value from {\tt functions}.
+
+@
+
+{\exercise
+We said in the recursion chapters that every recursive procedure has to have
+a base case and a recursive case, and that the recursive case has to somehow
+reduce the size of the problem, getting closer to the base case.  How does
+the recursive call in {\tt get-fn} reduce the size of the problem?
+}
+
+\solution
+
+Since {\tt get-fn} has no arguments, the recursive call is exactly the
+same as the original call, so the problem doesn't get smaller.  What
+saves us is that even the original problem {\it might\/} be solved
+without getting smaller at all, because it depends on the behavior of
+the human user of the program.  At each recursive invocation, the
+problem is this:  ``If the user enters a correct function, we're done.
+If not, try again.''  If the user makes a mistake, then the procedure
+calls itself recursively, with exactly the same problem.  And, indeed,
+this {\it can\/} be an infinite loop, if the user keeps typing incorrect
+functions.  But as soon as the user enters a correct function, the process
+will terminate.
+
+% A call to {\tt get-fn} looks like {\tt (get-fn)}, because the procedure
+% takes zero arguments.  So the recursive call, {\tt (get-fn)} is exactly the
+% same as the original call.  There are two ways to think about this.
+% 
+% The first is ``The size of the problem {\it doesn't\/} get smaller.'' Since
+% each call to {\tt get-fn} has the same arguments, the problem never changes,
+% and there's an infinite loop.  Indeed, if you invoke {\tt get-fn} and then
+% sit in front of the computer forever typing incorrect functions, the {\tt
+% get-fn} procedure will never return a value.
+% 
+% The second way to think about this is ``even though the arguments never
+% change, the state of the world changes between recursive calls, so
+% eventually the program will return a value.'' In this case, the progress
+% made by each recursive call is sort of bizarre:  after each time the user
+% incorrectly types the name of a function, he or she is more likely to get it
+% right the next time.
+
+@
+\bye