diff options
author | elioat <elioat@tilde.institute> | 2023-08-23 07:52:19 -0400 |
---|---|---|
committer | elioat <elioat@tilde.institute> | 2023-08-23 07:52:19 -0400 |
commit | 562a9a52d599d9a05f871404050968a5fd282640 (patch) | |
tree | 7d3305c1252c043bfe246ccc7deff0056aa6b5ab /js/games/nluqo.github.io/~bh/ssch20 | |
parent | 5d012c6c011a9dedf7d0a098e456206244eb5a0f (diff) | |
download | tour-562a9a52d599d9a05f871404050968a5fd282640.tar.gz |
*
Diffstat (limited to 'js/games/nluqo.github.io/~bh/ssch20')
-rw-r--r-- | js/games/nluqo.github.io/~bh/ssch20/io | 1333 | ||||
-rw-r--r-- | js/games/nluqo.github.io/~bh/ssch20/io.html | 1143 | ||||
-rw-r--r-- | js/games/nluqo.github.io/~bh/ssch20/part6.html | 111 |
3 files changed, 2587 insertions, 0 deletions
diff --git a/js/games/nluqo.github.io/~bh/ssch20/io b/js/games/nluqo.github.io/~bh/ssch20/io new file mode 100644 index 0000000..c5e9623 --- /dev/null +++ b/js/games/nluqo.github.io/~bh/ssch20/io @@ -0,0 +1,1333 @@ +\input bkmacs +\pagetag{\typing} +\photo{}{\pspicture{4in}{io}{io}{}\vfill} +\chapter{Input and Output} +\chaptag{\io} + + +In the tic-tac-toe project in Chapter \ttt, we didn't write a complete game +program. We wrote a {\it function\/} that took a board position and {\tt x} +or {\tt o} as arguments, returning the next move. We noted at the time that +a complete game program would also need to carry on a {\it conversation\/} +with the user. Instead of computing and returning one single value, a +\bkidx{conversational}{program} must carry out a sequence of events in time, +reading information from the \idx{keyboard} and displaying other information +on the \idx{screen}. + +Before we complete the tic-tac-toe project, we'll start by +exploring Scheme's mechanisms for \bkidx{interactive}{programming}. + +\backskipsubhd{Printing}{5} +\justidx{printing} + +Up until now, we've never told Scheme to print anything. The programs we've +written have computed values and returned them; we've relied on the +\idx{read-eval-print loop} to print these values.\footnt{The only +exception is that we've used {\tt trace}, which prints messages about +the progress of a computation.} + +But let's say we want to write a program to print out all of the words to +``99 Bottles of Beer on the Wall.'' We could implement a function to produce a +humongous {\it list\/} of the lines of the song, like this: + +{\prgex% +(define (bottles n) + (if (= n 0) + '() + (append (verse n) + (bottles (- n 1))))) +} + +{\medskipamount=4pt\prgexskipamount=9pt\prgexbaselineamount=10pt +{\prgex% +(define (verse n) + (list (cons n '(bottles of beer on the wall)) + (cons n '(bottles of beer)) + '(if one of those bottles should happen to fall) + (cons (- n 1) '(bottles of beer on the wall)) + '())) + +> (bottles 3) +((3 BOTTLES OF BEER ON THE WALL) + (3 BOTTLES OF BEER) + (IF ONE OF THOSE BOTTLES SHOULD HAPPEN TO FALL) + (2 BOTTLES OF BEER ON THE WALL) + () + (2 BOTTLES OF BEER ON THE WALL) + (2 BOTTLES OF BEER) + (IF ONE OF THOSE BOTTLES SHOULD HAPPEN TO FALL) + (1 BOTTLES OF BEER ON THE WALL) + () + (1 BOTTLES OF BEER ON THE WALL) + (1 BOTTLES OF BEER) + (IF ONE OF THOSE BOTTLES SHOULD HAPPEN TO FALL) + (0 BOTTLES OF BEER ON THE WALL) + ()) +} + +\noindent The problem is that we don't want a list. All we +want is to print out the lines of the song; storing them in a data structure +is unnecessary and inefficient. Also, some versions of Scheme would print +the above list like this: + +{\prgex% +((3 BOTTLES OF BEER ON THE WALL) (3 BOTTLES OF BEER) (IF ONE OF + THOSE BOTTLES SHOULD HAPPEN TO FALL) (2 BOTTLES OF BEER ON THE + WALL) () (2 BOTTLES OF BEER ON THE WALL) (2 BOTTLES OF BEER) (IF + ONE OF THOSE BOTTLES SHOULD HAPPEN TO FALL) (1 BOTTLES OF BEER ON + THE WALL) () (1 BOTTLES OF BEER ON THE WALL) (1 BOTTLES OF BEER) + (IF ONE OF THOSE BOTTLES SHOULD HAPPEN TO FALL) (0 BOTTLES OF BEER + ON THE WALL) ()) +} + +\noindent or even all on one line. We can't rely on Scheme's mechanism for +printing lists if we want to be sure of a particular arrangement on the +screen. + +Instead we'll write a program to {\it print\/} a verse, rather +than return it in a list: +\justtt{show} + +{\prgex% +(define (\ufun{bottles} n) + (if (= n 0) + 'burp + (begin (verse n) + (bottles (- n 1))))) + +(define (\ufun{verse} n) + (show (cons n '(bottles of beer on the wall))) + (show (cons n '(bottles of beer))) + (show '(if one of those bottles should happen to fall)) + (show (cons (- n 1) '(bottles of beer on the wall))) + (show '())) + +> (bottles 3) +(3 BOTTLES OF BEER ON THE WALL) +(3 BOTTLES OF BEER) +(IF ONE OF THOSE BOTTLES SHOULD HAPPEN TO FALL) +(2 BOTTLES OF BEER ON THE WALL) +() +(2 BOTTLES OF BEER ON THE WALL) +(2 BOTTLES OF BEER) +(IF ONE OF THOSE BOTTLES SHOULD HAPPEN TO FALL) +(1 BOTTLES OF BEER ON THE WALL) +() +(1 BOTTLES OF BEER ON THE WALL) +(1 BOTTLES OF BEER) +(IF ONE OF THOSE BOTTLES SHOULD HAPPEN TO FALL) +(0 BOTTLES OF BEER ON THE WALL) +() +BURP +} + +} % skip kludges + +\noindent Notice that Scheme doesn't print an outer set of parentheses. +Each line was printed separately; there isn't one big list containing all of +them.\footnt{We know that it's still not as beautiful as can be, because of +the capital letters and parentheses, but we'll get to that later.} + +Why was ``burp''\ printed at the end? Just because we're printing things +explicitly doesn't mean that the read-eval-print loop stops functioning. We +typed the expression {\tt (bottles~3)}. In the course of evaluating that +expression, Scheme printed several lines for us. But the {\it value\/} of +the expression was the word {\tt burp}, because that's what {\tt bottles} +returned. + +\subhd{Side Effects and Sequencing} + +How does our program work? There are two new ideas here:\ {\it +\bkidx{side}{effect}s\/} and {\it sequencing.} + +Until now, whenever we've invoked a procedure, our only goal has been to get +a return value. The procedures we've used compute and return a value, and do +nothing else. {\tt Show} is different. Although every Scheme procedure +returns a value, the Scheme language standard doesn't specify what +value the printing procedures should return.\footnt{Suppose {\tt show} +returns {\tt \#f} in your version of Scheme. Then you might see + +{\prgex% +> (show 7) +7 +#F +} + +\noindent But since the return value is unspecified, we try to write +programs in such a way that we never use {\tt show}'s return value as the +return value from our procedures. That's why we return values like {\tt +burp}.} Instead, we are interested in their side effects. In other words, +we invoke {\tt show} because we want it to {\it do\/} something, namely, +print its argument on the screen. + +What exactly do we mean by ``side effect''? The kinds of procedures that +we've used before this chapter can compute values, invoke helper procedures, +provide arguments to the helper procedures, and return a value. There may be +a lot of activity going on within the procedure, but the procedure +affects the world outside of itself only by returning a value that some +other procedure might use. {\tt Show} affects the world outside of itself +by putting something on the screen. After {\tt show} has finished its +work, someone who looks at the screen can tell that {\tt show} was +used.\footnt{The term {\it side\/} effect is based on the idea that a +procedure may have a useful return value as its main purpose and may also +have an effect ``on the side.'' It's a misnomer to talk about the +side effect of {\tt show}, since the effect is its main purpose. But nobody +ever says ``side return value''!} + +{\medskipamount=4pt\prgexskipamount=9pt\prgexbaselineamount=10pt +\def\psate{\prgexskipamount=8pt} + +Here's an example to illustrate the difference between values and effects: + +{\prgex% +(define (\ufun{effect} x) + (show x) + 'done) + +(define (\ufun{value} x) + x) + +> (effect '(oh! darling)) +(OH! DARLING) +DONE + +> (value '(oh! darling)) +(OH! DARLING) + +> (bf (effect '(oh! darling))) +(OH! DARLING) +ONE\psate + +> (bf (value '(oh! darling))) +(DARLING) + +> (define (\ufun{lots-of-effect} x) + (effect x) + (effect x) + (effect x)) + +> (define (\ufun{lots-of-value} x) + (value x) + (value x) + (value x)) + +> (lots-of-effect '(oh! darling)) +(OH! DARLING) +(OH! DARLING) +(OH! DARLING) +DONE + +> (lots-of-value '(oh! darling)) +(OH! DARLING) +} + +} % skip kludge + +This example also demonstrates the second new idea, +\idx{sequencing}: Each of {\tt effect}, {\tt lots-of-effect}, +and {\tt lots-of-value} contains more than one expression in +its body. When you invoke such a procedure, Scheme evaluates all +the expressions in the body, in order, and returns the value of the +last one.\footnt{In Chapter \defining, we said that the body of a +procedure was always one single expression. We lied. But as long +as you don't use any procedures with side effects, it doesn't do you +any good to evaluate more than one expression in a body.} This also +works in the body of a {\tt let}, which is really the body of a +procedure, and in each clause of a {\ttidx cond}.\footnt{For example: + +{\zfprgex% +> (cond ((< 4 0) + (show '(how interesting)) + (show '(4 is less than zero?)) + #f) + ((> 4 0) + (show '(more reasonable)) + (show '(4 really is more than zero)) + 'value) + (else + (show '(you mean 4=0?)) + #f)) +(MORE REASONABLE) +(4 REALLY IS MORE THAN ZERO) +VALUE}} + +When we invoked {\tt lots-of-value}, Scheme invoked {\tt value} three times; +it discarded the values returned by the first two invocations, and returned +the value from the third invocation. Similarly, when we invoked {\tt +lots-of-effect}, Scheme invoked {\tt effect} three times and returned the +value from the third invocation. But each invocation of {\tt effect} caused +its argument to be printed by invoking {\tt show}. + +\subhd{The \ttpmb{Begin} Special Form} +\pagetag{\beg} + +The {\tt lots-of-effect} procedure accomplished sequencing by having more +than one expression in its body. This works fine if the sequence of events +that you want to perform is the entire body of a procedure. But in {\tt +bottles} we wanted to include a sequence as one of the alternatives in an +{\tt if} construction. We couldn't just say + +{\prgex% +(define (bottles n) ;; wrong + (if (= n 0) + '() + (verse n) + (bottles (- n 1)))) +} + +\noindent because {\tt if} must have exactly three arguments. Otherwise, +how would {\tt if} know whether we meant {\tt (verse~n)} to be the second +expression in the true case, or the first expression in the false case? + +Instead, to turn the sequence of expressions into a single expression, we +use the \bkidx{special}{form} \ttidx{begin}. It takes any number of +arguments, evaluates them from left to right, and returns the value of the +last one. + +{\prgex% +(define bottles n) + (if (= n 0) + 'burp + (begin (verse n) + (bottles (- n 1))))) +} + +\noindent (One way to think about sequences in procedure bodies is that +every procedure body has an invisible {\tt begin} surrounding it.) + +\subhd{This Isn't Functional Programming} + +Sequencing and side effects are radical departures from the idea of +\bkidx{functional}{programming}. In fact, we'd like to reserve the name {\it +function\/} for something that computes and returns one value, with no side +effects. ``Procedure'' is the general term for the thing that {\tt lambda} +returns---an embodiment of an algorithm. If the algorithm is the kind that +computes and returns a single value without side effects, then we say that +the procedure implements a function.\footnt{Sometimes people sloppily +say that the procedure {\it is\/} a function. In fact, you may hear people +be {\it really\/} sloppy and call a non-functional procedure a function!} + +There is a certain kind of sequencing even in functional programming. If +you say + +{\prgex% +(* (+ 3 4) (- 92 15)) +} + +\noindent it's clear that the addition has to happen before the +multiplication, because the result of the addition provides one of the +arguments to the multiplication. What's new in the sequential programming +style is the {\it emphasis\/} on sequence, and the fact that the expressions +in the sequence are {\it independent\/} instead of contributing values to +each other. In this multiplication problem, for example, we don't care +whether the addition happens before or after the subtraction. If the +addition and subtraction were in a sequence, we'd be using them for +independent purposes: + +{\prgex% +(begin + (show (+ 3 4)) + (show (- 92 15))) +} + +\noindent This is what we mean by being independent. Neither expression +helps in computing the other. And the order matters because we can see +the order in which the results are printed. + +\subhd{Not Moving to the Next Line} + +Each invocation of {\tt show} prints a separate line. What if we +want a program that prints several things on the same line, like this: + +{\prgex% +> (begin (show-addition 3 4) + (show-addition 6 8) + 'done) +3+4=7 +6+8=14 +DONE +} + +\noindent We use \ttidx{display}, which doesn't move to the next line after +printing its argument: + +{\prgex% +(define (\ufun{show-addition} x y) + (display x) + (display '+) + (display y) + (display '=) + (show (+ x y))) +} + +\noindent (The last one is a {\tt show} because we {\it do\/} want to start +a new line after it.) + +What if you just want to print a blank line? You use \ttidx{newline}: + +{\prgex% +(define (verse n) + (show (cons n '(bottles of beer on the wall))) + (show (cons n '(bottles of beer))) + (show '(if one of those bottles should happen to fall)) + (show (cons (- n 1) '(bottles of beer on the wall))) + (newline)) ; replaces (show '()) +} + +In fact, {\tt show} isn't an official Scheme primitive; we wrote it +in terms of {\tt display} and {\tt newline}. + +\subhd{Strings} + +Throughout the book we've occasionally used strings, that is, words enclosed in +double-quote marks so that Scheme will permit the use of punctuation or other +unusual characters. Strings also preserve the case of letters, so they can +be used to beautify our song even more. Since {\it any\/} character can be +in a \idx{string}, including spaces, the easiest thing to do in this case is +to treat all the letters, spaces, and punctuation characters of each line of +the song as one long word. (If we wanted to be able to compute functions of +the individual words in each line, that wouldn't be such a good idea.) + +{\prgex% +(define (\ufun{verse} n) + (display n) + (show " bottles of beer on the wall,") + (display n) + (show " bottles of beer.") + (show "If one of those bottles should happen to fall,") + (display (- n 1)) + (show " bottles of beer on the wall.") + (newline)) +} + +{\medskipamount=4pt\prgexskipamount=8pt\prgexbaselineamount=10pt +{\prgex% +> (verse 6) +6 bottles of beer on the wall, +6 bottles of beer. +If one of those bottles should happen to fall, +5 bottles of beer on the wall. + +#F ; or whatever is returned by (newline) +} + +% \def\vb{\vispc\penalty 0{}} +% \def\vb{\_} +\def\vb{\ } +\noindent It's strange to think of ``{\tt\vb bottles\vb of\vb +beer\vb on\vb the\vb wall,}'' as a single word. But the rule is that +anything inside double quotes counts as a single word. It doesn't have to +be an English word. + +\backskipsubhd{A Higher-Order Procedure for Sequencing}{8} + +Sometimes we want to print each element of a list separately: + +{\prgex% +(define (\ufun{show-list} lst) + (if (null? lst) + 'done + (begin (show (car lst)) + (show-list (cdr lst))))) + +> (show-list '((dig a pony) (doctor robert) (for you blue))) +(DIG A PONY) +(DOCTOR ROBERT) +(FOR YOU BLUE) +DONE +} + +Like other patterns of computation involving lists, this one can be +abstracted into a higher-order procedure. (We can't call it a +``higher-order function'' because this one is for computations with side +effects.) The procedure {\tt \ttidx{for-each}} is part of standard Scheme: + +{\prgex% +> (for-each show '((mean mr mustard) (no reply) (tell me why))) +(MEAN MR MUSTARD) +(NO REPLY) +(TELL ME WHY) +} + +\noindent The value returned by {\tt for-each} is unspecified. + +Why couldn't we just use {\tt map} for this purpose? There are two reasons. +One is just an efficiency issue: {\tt Map} constructs a list containing the +values returned by each of its sub-computations; in this example, it would +be a list of three instances of the unspecified value returned by {\tt +show}. But we aren't going to use that list for anything, so there's no +point in constructing it. The second reason is more serious. In functional +programming, the order of evaluation of subexpressions is unspecified. For +example, when we evaluate the expression + +{\prgex% +(- (+ 4 5) (* 6 7)) +} + +\noindent we don't know whether the addition or the multiplication happens +first. Similarly, the order in which {\tt map} computes the results for +each element is unspecified. That's okay as long as the ultimately returned +list of results is in the right order. But when we are using side effects, +we {\it do\/} care about the order of evaluation. In this case, we want +to make sure that the elements of the argument list are printed from left to +right. {\tt For-each} guarantees this ordering. + +\backskipsubhd{Tic-Tac-Toe Revisited}{8} + +We're working up toward playing a game of tic-tac-toe against the computer. +But as a first step, let's have the computer play against itself. What we +already have is {\tt ttt}, a {\it strategy\/} function:\ one that takes a +board position as argument (and also a letter {\tt x} or {\tt o}) and +returns the chosen next move. In order to play a game of tic-tac-toe, we +need two players; to make it more interesting, each should have its own +strategy. So we'll write another one, quickly, that just moves in the first +empty square it sees: + +{\prgex% +(define (\ufun{stupid-ttt} position letter) + (location '_ position)) + +(define (\ufun{location} letter word) + (if (equal? letter (first word)) + 1 + (+ 1 (location letter (bf word))))) +} + +Now we can write a program that takes two strategies as arguments and +actually plays a game between them. + +{\prgex% +(define (\ufun{play-ttt} x-strat o-strat) + (play-ttt-helper x-strat o-strat '_________ 'x)) + +(define (\ufun{play-ttt-helper} x-strat o-strat position whose-turn) + (cond ((already-won? position (opponent whose-turn)) + (list (opponent whose-turn) 'wins!)) + ((tie-game? position) '(tie game)) + (else (let ((square (if (equal? whose-turn 'x) + (x-strat position 'x) + (o-strat position 'o)))) + (play-ttt-helper x-strat + o-strat + (add-move square whose-turn position) + (opponent whose-turn)))))) +} +} % skip kludge + +\noindent We use a helper procedure because we need to keep track of two +pieces of information besides the strategy procedures:\ the current board +position and whose turn it is ({\tt x} or {\tt o}). The helper procedure +is invoked recursively for each move. First it checks whether the game +is already over (won or tied).\footnt{You wrote the procedures {\tt +already-won?}\ and {\tt tie-game?}\ in Exercises \tttwon\ and \ttttied: + +{\prgex% +(define (\ufun{already-won?} position who) + (member? (word who who who) (find-triples position))) + +(define (\ufun{tie-game?} position) + (not (member? '_ position))) +}} +If not, the helper procedure invokes the current player's strategy procedure, +which returns the square number for the next move. For the recursive call, +the arguments are the same two strategies, the new position after the move, +and the letter for the other player. + +We still need {\tt add-move}, the procedure that takes a square and an old +position as arguments and returns the new position. + +{\prgex% +(define (\ufun{add-move} square letter position) + (if (= square 1) + (word letter (bf position)) + (word (first position) + (add-move (- square 1) letter (bf position))))) + +> (play-ttt ttt stupid-ttt) +(X WINS!) + +> (play-ttt stupid-ttt ttt) +(O WINS!) +} + +\subhd{Accepting User Input} + +The work we did in the last section was purely functional. We didn't print +anything (except the ultimate return value, as always) and we didn't +have to read information from a human player, because there wasn't one. + +You might expect that the structure of an {\it interactive\/} game program +would be very different, with a top-level procedure full of sequential +operations. But the fact is that we hardly have to change anything to turn +this into an interactive game. All we need is a new ``strategy'' procedure +that asks the user where to move, instead of computing a move based on +built-in rules. + +{\prgex% +(define (\ufun{ask-user} position letter) + (print-position position) + (display letter) + (display "'s move: ") + (read)) + +(define (print-position position) ;; first version + (show position)) +} + +\noindent (Ultimately we're going to want a beautiful two-dimensional +display of the current position, but we don't want to get distracted by that +just now. That's why we've written a trivial temporary version.) + +{\prgex% +> (play-ttt ttt ask-user) +____X____ +O'S MOVE: \pmb{1} +O___XX___ +O'S MOVE: \pmb{4} +O__OXXX__ +O'S MOVE: \pmb{3} +OXOOXXX__ +O'S MOVE: \pmb{8} +(TIE GAME) +} + +\noindent What the user typed is just the single digits shown in boldface at +the ends of the lines. + +What's new here is that we invoke the procedure \ttidx{read}. It waits for +you to type a Scheme expression, and returns that expression. Don't +be confused: {\tt Read} does {\it not\/} evaluate what you type. It +returns exactly the same expression that you type: + +{\prgex% +(define (\ufun{echo}) + (display "What? ") + (let ((expr (read))) + (if (equal? expr 'stop) + 'okay + (begin + (show expr) + (echo))))) +} + +{\medskipamount=2pt\prgexskipamount=7.5pt\prgexbaselineamount=10pt +{\prgex% +> (echo) +What? \pmb{hello} +HELLO +What? \pmb{(+ 2 3)} +(+ 2 3) +What? \pmb{(first (glass onion))} +(FIRST (GLASS ONION)) +What? \pmb{stop} +OKAY +} + +\backskipsubhd{Aesthetic Board Display}{9} + +Here's our beautiful position printer: + +{%\setbox2=\hbox{{\ninett +}}\catcode`+=\active\def+{\lower1pt\copy2} + +{\prgex% +(define (\ufun{print-position} position) + (print-row (subword position 1 3)) + (show "-+-+-") + (print-row (subword position 4 6)) + (show "-+-+-") + (print-row (subword position 7 9)) + (newline)) + +(define (\ufun{print-row} row) + (maybe-display (first row)) + (display "|") + (maybe-display (first (bf row))) + (display "|") + (maybe-display (last row)) + (newline)) + +(define (\ufun{maybe-display} letter) + (if (not (equal? letter '_)) + (display letter) + (display " "))) + +(define (\ufun{subword} wd start end) + ((repeated bf (- start 1)) + ((repeated bl (- (count wd) end)) + wd)))\pgfoot +\nobreak} +\nobreak\vfootnt{Alternate version: +\def\fpskip{\vskip 5pt\relax} +{\prgex\fprgexbaselineamount=9.5pt% +(define (subword wd start end) + (cond ((> start 1) (subword (bf wd) (- start 1) (- end 1))) + ((< end (count wd)) (subword (bl wd) start end)) + (else wd))) +} + +\noindent You can take your choice, depending on which you think is easier, +recursion or higher-order functions. +} +} % skip kludge + +Here's how it works: + +{\prgex% +> (print-position '_x_oo__xx) + |X| +-+-+- +O|O| +-+-+- + |X|X +} +} %%%%%%%%% active + kludge %%%%%%%%%%%%% + +\subhd{Reading and Writing Normal Text} + +The {\tt read} procedure works fine as long as what you type looks like a +Lisp program. That is, it reads one expression at a time. In the +tic-tac-toe program the user types a single number, which is a Scheme +expression, so {\tt read} works fine. But what if we want to read more than +one word? + +{\prgex% +(define (music-critic) ;; first version + (show "What's your favorite Beatles song?") + (let ((song (read))) + (show (se "I like" song "too.")))) + +> (music-critic) +What's your favorite Beatles song? +\pmb{She Loves You} +(I like SHE too.) +} + +\noindent If the user had typed the song title in parentheses, then it would +have been a single Scheme expression and {\tt read} would have accepted it. +But we don't want the users of our program to have to be typing parentheses +all the time. + +Scheme also lets you read one character at a time. This allows you to read +any text, with no constraints on its format. The disadvantage is that you +find yourself putting a lot of effort into minor details. We've provided a +procedure {\tt \ttidx{read-line}} that reads one line of input and returns a +sentence. The words in that sentence will contain any punctuation +characters that appear on the line, including parentheses, which are not +interpreted as sublist delimiters by {\tt read-line}. {\tt Read-line} also +preserves the case of letters. + +{\prgex% +(define (music-critic) ;; second version + (read-line) ; See explanation on next page. + (show "What's your favorite Beatles song?") + (let ((song (read-line))) + (show (se "I like" song "too.")))) + +> (music-critic) +What's your favorite Beatles song? +\pmb{She Loves You} +(I like She Loves You too.) +} + +\noindent Why do we call {\tt read-line} and ignore its result at the +beginning of {\tt music-critic}? It has to do with the interaction between +{\tt read-line} and {\tt read}. {\tt Read} treats what you type as a +sequence of Scheme expressions; each invocation of {\tt read} reads one of +them. {\tt Read} pays no attention to formatting details, such as several +consecutive spaces or line breaks. If, for example, you type several +expressions on the same line, it will take several invocations of {\tt read} +to read them all. + +By contrast, {\tt read-line} treats what you type as a sequence of lines, +reading one line per invocation, so it does pay attention to line breaks. + +Either of these ways to read input is sensible in itself, but if you mix +the two, by invoking {\tt read} sometimes and {\tt read-line} sometimes in +the same program, the results can be confusing. Suppose you type a line +containing an expression and your program invokes {\tt read} to read it. +Since there might have been another expression on the line, {\tt read} +doesn't advance to the next line until you ask for the next +expression. So if you now invoke {\tt read-line}, thinking that it will +read another line from the keyboard, it will instead return an empty list, +because what it sees is an empty line---what's left after {\tt read} uses up +the expression you typed. + +You may be thinking, ``But {\tt music-critic} doesn't call {\tt read}!'' +That's true, but Scheme itself used {\tt read} to read the expression that +you used to invoke {\tt music-critic}. So the first invocation of {\tt +read-line} is needed to skip over the spurious empty line. + +Our solution works only if {\tt music-critic} is invoked directly at a +Scheme prompt. If {\tt music-critic} were a subprocedure of some larger +program that has already called {\tt read-line} before calling {\tt +music-critic}, the extra {\tt read-line} in {\tt music-critic} would really +read and ignore a useful line of text. + +If you write a procedure using {\tt read-line} that will sometimes be called +directly and sometimes be used as a subprocedure, you can't include an extra +{\tt read-line} call in it. Instead, when you call your procedure directly +from the Scheme prompt, you must say + +{\prgex% +> (begin (read-line) (my-procedure)) +} + +Another technical detail about {\tt read-line} is that since +it preserves the capitalization of words, its result may +include strings, which will be shown in quotation marks if you return the +value rather than {\tt show}ing it: + +{\prgex% +(define (music-critic-return) + (read-line) + (show "What's your favorite Beatles song?") + (let ((song (read-line))) + (se "I like" song "too."))) + +> (music-critic-return) +What's your favorite Beatles song? +\pmb{She Loves You} +("I like" "She" "Loves" "You" "too.") +} + +We have also provided {\tt \ttidx{show-line},} which takes a sentence +as argument. It prints the sentence without surrounding parentheses, +followed by a newline. (Actually, it takes any list as argument; it prints +all the parentheses except for the outer ones.) + +{\prgex% +(define (\ufun{music-critic}) + (read-line) + (show "What's your favorite Beatles song?") + (let ((song (read-line))) + (show-line (se "I like" song "too.")))) + +> (music-critic) +What's your favorite Beatles song? +\pmb{She Loves You} +I like She Loves You too. +} + +The difference between {\tt show} and {\tt show-line} isn't +crucial. It's just a matter of a pair of parentheses. The point is that +{\tt read-line} and {\tt show-line} go together. {\tt Read-line} reads a +bunch of disconnected words and combines them into a sentence. {\tt +Show-line} takes a sentence and prints it as if it were a bunch of +disconnected words. Later, when we read and write files in Chapter +\files, this ability to print in the same form in which we read will be +important. + +\subhd{Formatted Text} + +We've been concentrating on the use of sequential programming with explicit +\pagetag{\spformat} +printing instructions for the sake of conversational programs. Another +common application of sequential printing is to display tabular information, +such as columns of numbers. The difficulty is to get the numbers to line up +so that corresponding digits are in the same position, even when the numbers +have very widely separated values. The +\ttidx{align} function can be used to convert a number to a printable word +with a fixed number of positions before and after the decimal point: + +{\prgex% +(define (square-root-table nums) + (if (null? nums) + 'done + (begin (display (align (car nums) 7 1)) + (show (align (sqrt (car nums)) 10 5)) + (square-root-table (cdr nums))))) + +> (square-root-table '(7 8 9 10 20 98 99 100 101 1234 56789)) + 7.0 2.64575 + 8.0 2.82843 + 9.0 3.00000 + 10.0 3.16228 + 20.0 4.47214 + 98.0 9.89949 + 99.0 9.94987 + 100.0 10.00000 + 101.0 10.04988 + 1234.0 35.12834 +56789.0 238.30443 +DONE +} + +\noindent {\tt Align} takes three arguments. The first is the value to be +displayed. The second is the width of the column in which it will be +displayed; the returned value will be a word with that many characters in it. +The third argument is the number of digits that should be displayed to the +right of the decimal point. (If this number is zero, then no decimal point +will be displayed.) The width must be great enough to include all the +digits, as well as the decimal point and minus sign, if any. + +As the program example above indicates, {\tt align} does not print +anything. It's a function that returns a value suitable for printing with +{\tt display} or {\tt show}. + +What if the number is too big to fit in the available space? + +{\prgex% +> (align 12345679 4 0) +"123+" +} + +\noindent {\tt Align} returns a word containing the first few digits, +as many as fit, ending with a plus sign to indicate that part of the value +is missing. + +{\tt Align} can also be used to include non-numeric text in columns. If +the first argument is not a number, then only two arguments are needed; the +second is the column width. In this case {\tt align} returns a word with +extra spaces at the right, if necessary, so that the argument word will +appear at the left in its column: + +{\prgex% +(define (\ufun{name-table} names) + (if (null? names) + 'done + (begin (display (align (cadar names) 11)) + (show (caar names)) + (name-table (cdr names))))) + +> (name-table '((john lennon) (paul mccartney) + (george harrison) (ringo starr))) +LENNON JOHN +MCCARTNEY PAUL +HARRISON GEORGE +STARR RINGO +DONE +} + +\noindent As with numbers, if a non-numeric word won't fit in the allowed +space, {\tt align} returns a partial word ending with a plus sign. + +This {\tt align} function is not part of standard Scheme. Most programming +languages, including some versions of Scheme, offer much more elaborate +formatting capabilities with many alternate ways to represent both numbers +and general text. Our version is a minimal capability to show the flavor +and to meet the needs of projects in this book. + +\subhd{Sequential Programming and Order of Evaluation} + +Our expanded tic-tac-toe program includes both functional and sequential +parts. The program computes its strategy functionally but uses sequences +of commands to control the {\it \bkidx{user}{interface}\/} by alternately +printing information to the screen and reading information from the keyboard. + +By adding sequential programming to our toolkit, we've increased our ability +to write interactive programs. But there is a cost that goes along with +this benefit: We now have to pay more attention to the order of events than +we did in purely functional programs. + +The obvious concern about order of events is that sequences of {\tt show} +expressions must come in the order in which we want them to appear, and {\tt +read} expressions must fit into the sequence properly so that the user is +asked for the right information at the right time. + +But there is another, less obvious issue about order of events. When the +evaluation of expressions can have side effects in addition to returning +values, the order of evaluation of argument subexpressions becomes important. +Here's an example to show what we mean. Suppose we type the expression + +{\prgex% +(list (+ 3 4) (- 10 2)) +} + +\noindent The answer, of course, is {\tt (7~8)}. It doesn't matter whether +Scheme computes the seven first (left to right) or the eight first (right to +left). But here's a similar example in which it {\it does\/} matter: + +{\prgex% +(define (\ufun{show-and-return} x) + (show x) + x) + +> (list (show-and-return (+ 3 4)) (show-and-return (- 10 2))) +8 +7 +(7 8) +} + +\noindent The value that's ultimately returned, in this example, is the same +as before. But the two numeric values that go into the list are also +printed separately, so we can see which is computed first. (We've shown +the case of right-to-left computation; your Scheme might be different.) + +Suppose you want to make sure that the seven prints first, regardless of +which order your Scheme uses. You could do this: + +{\prgex% +> (let ((left (show-and-return (+ 3 4)))) + (list left (show-and-return (- 10 2)))) +7 +8 +(7 8) +} + +\noindent The expression in the body of a {\tt let} can't be evaluated until +the {\tt let} variables (such as {\tt left}) have had their values computed. + +It's hard to imagine a practical use for the artificial {\tt +show-and-return} procedure, but a similar situation arises whenever we use +{\tt read}. Suppose we want to write a procedure to ask a person for his or +her full name, returning a two-element list containing the first and last +name. A natural mistake to make would be to write this procedure: + +{\prgex% +(define (ask-for-name) ;; wrong + (show "Please type your first name, then your last name:") + (list (read) (read))) + +> (ask-for-name) +Please type your first name, then your last name: +\pmb{John +Lennon} +(LENNON JOHN) +} + +\noindent What went wrong? We happen to be using a version of Scheme that +evaluates argument subexpressions from right to left. Therefore, the word +{\tt John} was read by the rightmost call to {\tt read}, which provided the +second argument to {\tt list}. The best solution is to use {\tt let} as we +did above: + +{\prgex% +(define (\ufun{ask-for-name}) + (show "Please type your first name, then your last name:") + (let ((first-name (read))) + (list first-name (read)))) +} + +Even this example looks artificially simple, because of the two invocations +of {\tt read} that are visibly right next to each other in the erroneous +version. But look at {\tt play-ttt-helper}. The word {\tt read} doesn't +appear in its body at all. But when we invoke it using {\tt ask-user} as +the strategy procedure for {\tt x}, the expression + +{\prgex% +(x-strat position 'x) +} + +\noindent hides an invocation of {\tt read}. The structure of {\tt +play-ttt-helper} includes a {\tt let} that controls the timing of that {\tt +read}. (As it turns out, in this particular case we could have gotten away +with writing the program without {\tt let}. The hidden invocation of {\tt +read} is the only subexpression with a side effect, so there aren't two +effects that might get out of order. But we had to think carefully about +the program to be sure of that.) + +\subhd{Pitfalls} + +\pit It's easy to get confused about what is printed explicitly by your +\justidx{printing} +program and what is printed by Scheme's read-eval-print loop. Until now, +{\it all\/} printing was of the second kind. Here's an example that doesn't +do anything very interesting but will help make the point clear: + +{\prgex% +(define (name) + (display "MATT ") + 'wright) + +> (name) +MATT WRIGHT +} + +\noindent At first glance it looks as if putting the word ``Matt'' inside a +call to {\tt display} is unnecessary. After all, the word {\tt wright} is +printed even without using {\tt display}. But watch this: + +{\prgex% +> (bf (name)) +MATT RIGHT +} + +\noindent Every time you invoke {\tt name}, whether or not as the entire +expression used at a Scheme prompt, the word {\tt MATT} is printed. But +the word {\tt wright} is {\it returned,\/} and may or may not be printed +depending on the context in which {\tt name} is invoked. + +\pit A sequence of expressions returns the value of the {\it last\/} +expression. If that isn't what you want, you must remember the value you +want to return using {\tt let}: + +{\prgex% +(let ((result (compute-this-first))) + (begin + (compute-this-second) + (compute-this-third) + result)) +} + +\pit Don't forget that the first call to {\tt read-line}, or any call to +{\tt read-line} after a call to {\tt read}, will probably read the empty +line that {\tt read} left behind. + +\pit Sometimes you want to use what the user typed more than once in your +program. But don't forget that {\tt read} has an effect as well as a return +value. Don't try to read the same expression twice: + +{\prgex% +(define (ask-question question) ;; wrong + (show question) + (cond ((equal? (read) 'yes) #t) + ((equal? (read) 'no) #f) + (else (show "Please answer yes or no.") + (ask-question question)))) +} + +\noindent If the answer is {\tt yes}, this procedure will work fine. But if +not, the second invocation of {\tt read} will read a second expression, not +test the same expression again as intended. To avoid this problem, invoke +{\tt read} only once for each expression you want to read, and use {\tt let} +to remember the result: + +{\prgex% +(define (\ufun{ask-question} question) + (show question) + (let ((answer (read))) + (cond ((equal? answer 'yes) #t) + ((equal? answer 'no) #f) + (else (show "Please answer yes or no.") + (ask-question question))))) +} + +\esubhd{Boring Exercises} + +{\exercise +What happens when we evaluate the following expression? What is printed, +and what is the return value? Try to figure it out in your head before you +try it on the computer. + +{\prgex% +(cond ((= 2 3) (show '(lady madonna)) '(i call your name)) + ((< 2 3) (show '(the night before)) '(hello little girl)) + (else '(p.s. i love you))) +}} + +\solution +{\tt (THE NIGHT BEFORE)} is printed. + +{\tt (HELLO LITTLE GIRL)} is returned. + +@ + +{\exercise +What does {\tt newline} return in your version of Scheme? +} + +{\exercise +Define {\tt show} in terms of {\tt newline} and {\tt display}. +} + +\solution +{\prgex% +(define (show stuff) + (display stuff) + (newline)) +} +@ + +\esubhd{Real Exercises} + +{\exercise +Write a program that carries on a conversation like the following example. +What the user types is in boldface. + +{\prgex% +> \pmb{(\ufun{converse})} +Hello, I'm the computer. What's your name? \pmb{Brian Harvey} +Hi, Brian. How are you? \pmb{I'm fine.} +Glad to hear it. +}} + +\solution + +Here's a boring version that's glad to hear anything about how you're +doing. Naturally you could make it do a lot more if you were so inclined. + +{\prgex% +(define (converse) + (read-line) + (display "Hello, I'm the computer. What's your name? ") + (let ((name (read-line))) + (display (word "Hi, " (first name) ". How are you? ")) + (read-line) + (show "Glad to hear it."))) +} +@ + +{\exercise +Our {\tt name-table} procedure uses a fixed width for the column containing +the last names of the people in the argument list. Suppose that instead of +liking British-invasion music you are into late romantic Russian composers: + +{\prgex% +> (name-table '((piotr tchaikovsky) (nicolay rimsky-korsakov) + (sergei rachmaninov) (modest musorgsky))) +} + +\noindent Alternatively, perhaps you like jazz: + +{\prgex% +> (name-table '((bill evans) (paul motian) (scott lefaro))) +} + +\noindent Modify {\tt name-table} so that it figures out the longest last +name in its argument list, adds two for spaces, and uses that number as the +width of the first column. +} + +\solution +{\prgex% +(define (name-table names) + (if (null? names) + 'done + (nt-help names + (+ 2 (reduce max (map (lambda (nm) (count (last nm))) + names)))))) + +(define (nt-help names width) + (if (null? names) + 'done + (begin (display (align (cadar names) width)) + (show (caar names)) + (nt-help (cdr names) width)))) +} + +The {\tt null?} test in {\tt name-table} is needed only for the case +in which the user gives an empty argument; the {\tt null?} test that +serves as the base case for the recursion is the one in {\tt nt-help}. + +@ + +{\exercise +The procedure {\tt ask-user} isn't robust. What happens if you type +something that isn't a number, or isn't between 1 and 9? Modify it to check +that what the user types is a number between 1 and 9. If not, it should +print a message and ask the user to try again. +} + +\solution +The changed parts of the procedure are shown here in boldface. + +{\prgex% +(define (ask-user position letter) + (print-position position) + (display letter) + (display "'s move: ") + \pmb{(let ((answer (read)))} + \pmb{(if (and (integer? answer) (>= answer 1) (<= answer 9))} + \pmb{answer} + \pmb{(begin (show "That's not a move, silly!")} + \pmb{(ask-user position letter)))))} +} +@ + +{\exercise +Another problem with {\tt ask-user} is that it allows a user to request a +square that isn't free. If the user does this, what happens? Fix {\tt +ask-user} to ensure that this can't happen. +} + +\solution +If the user asks for a square that's already taken, the square will +be reassigned to the user. The following solution assumes that the +previous exercise is also included. +The changed parts of the procedure are shown here in boldface. + + +{\prgex% +(define (ask-user position letter) + (print-position position) + (display letter) + (display "'s move: ") + (let ((answer (read))) + \pmb{(cond ((not} (and (integer? answer) (>= answer 1) (<= answer 9))) + (show "That's not a move, silly!") + (ask-user position letter)) + \pmb{((not (equal? '_ (item answer position)))} + \pmb{(show "That square is occupied.")} + \pmb{(ask-user position letter))} + \pmb{(else} answer)))) +} +@ + +{\exercise +At the end of the game, if the computer wins or ties, you never find out +which square it chose for its final move. Modify the program to correct +this. (Notice that this exercise requires you to make {\tt play-ttt-helper} +non-functional.) +} + +\solution +The changed parts of the procedure are shown here in boldface. + +{\prgex% +(define (play-ttt-helper x-strat o-strat position whose-turn) + (cond ((already-won? position (opponent whose-turn)) + \pmb{(print-position position)} + (list (opponent whose-turn) 'wins!)) + ((tie-game? position) + \pmb{(print-position position)} + '(tie game)) + (else (let ((square (if (equal? whose-turn 'x) + (x-strat position 'x) + (o-strat position 'o)))) + (play-ttt-helper x-strat + o-strat + (add-move square whose-turn position) + (opponent whose-turn)))))) +} +@ + +{\exercise +The way we invoke the game program isn't very user-friendly. Write a +procedure {\tt game} that asks you whether you wish to play {\tt x} or {\tt +o}, then starts a game. (By definition, {\tt x} plays first.) Then write a +procedure {\tt games} that allows you to keep playing repeatedly. It +can ask ``do you want to play again?''\ after each game. (Make sure that +the outcome of each game is still reported, and that the user can choose +whether to play {\tt x} or {\tt o} before each game.) +} + +\solution +{\prgex% +(define (game) + (display "Do you want to play X or O? ") + (let ((letter (read))) + (cond ((equal? letter 'x) + (play-ttt ask-user ttt)) + ((equal? letter 'o) + (play-ttt ttt ask-user)) + (else (show "Please type X or O!") + (game))))) + +(define (games) + (show (game)) + (display "Do you want to play again (Y or N)? ") + (if (another?) + (games) + "Thank you for playing, have a day.")) + +(define (another?) + (let ((letter (read))) + (cond ((equal? letter 'y) #t) + ((equal? letter 'n) #f) + (else (show "C'mon, Y or N!") + (another?))))) +} + +{\tt Game} and {\tt games} both ask a question, and both include a +check for invalid answers. But {\tt game} is able to repeat the +question itself, if the answer was invalid, whereas {\tt games} +uses a helper procedure {\tt another?} to ask the question. The +reason for this difference is that {\tt games} plays a game before +asking its question, whereas the question is the first thing in +{\tt game}. If {\tt games} were written as a single procedure, +an invalid answer would result in playing another game before +asking again. + +@ + +\bye diff --git a/js/games/nluqo.github.io/~bh/ssch20/io.html b/js/games/nluqo.github.io/~bh/ssch20/io.html new file mode 100644 index 0000000..0d04357 --- /dev/null +++ b/js/games/nluqo.github.io/~bh/ssch20/io.html @@ -0,0 +1,1143 @@ +<P> + +<P><A NAME="typing"></A> +<P><CENTER><IMG SRC="../ss-pics/io.jpg" ALT="figure: io"></CENTER> +<HTML> +<HEAD> +<TITLE>Simply Scheme: Introducing Computer Science ch 20: Input and Output</TITLE> +</HEAD> +<BODY> +<HR> +<CITE>Simply Scheme:</CITE> +<CITE>Introducing Computer Science</CITE> 2/e Copyright (C) 1999 MIT +<H2>Chapter 20</H2> +<H1>Input and Output</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/ssch20.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="part6.html"><STRONG>BACK</STRONG></A> +chapter thread <A HREF="../ssch21/functions-implement.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> +<P>In the tic-tac-toe project in Chapter 10, we didn't write a complete game +program. We wrote a <EM>function</EM> that took a board position and <CODE>x</CODE> +or <CODE>o</CODE> as arguments, returning the next move. We noted at the time that +a complete game program would also need to carry on a <EM>conversation</EM> +with the user. Instead of computing and returning one single value, a +<A NAME="g1"></A><A NAME="g2"></A>conversational program must carry out a sequence of events in time, +reading information from the keyboard and displaying other information +on the screen. + +<P>Before we complete the tic-tac-toe project, we'll start by +exploring Scheme's mechanisms for <A NAME="g3"></A><A NAME="g4"></A>interactive programming. + +<P><H2>Printing</H2> +<A NAME="g5"></A> + +<P>Up until now, we've never told Scheme to print anything. The programs we've +written have computed values and returned them; we've relied on the +read-eval-print loop to print these values.<A NAME="text1" HREF="io.html#ft1">[1]</A> + +<P>But let's say we want to write a program to print out all of the words to +"99 Bottles of Beer on the Wall." We could implement a function to produce a +humongous <EM>list</EM> of the lines of the song, like this: + +<P><PRE>(define (bottles n) + (if (= n 0) + '() + (append (verse n) + (bottles (- n 1))))) +</PRE> + +<P><PRE>(define (verse n) + (list (cons n '(bottles of beer on the wall)) + (cons n '(bottles of beer)) + '(if one of those bottles should happen to fall) + (cons (- n 1) '(bottles of beer on the wall)) + '())) + +> (bottles 3) +((3 BOTTLES OF BEER ON THE WALL) + (3 BOTTLES OF BEER) + (IF ONE OF THOSE BOTTLES SHOULD HAPPEN TO FALL) + (2 BOTTLES OF BEER ON THE WALL) + () + (2 BOTTLES OF BEER ON THE WALL) + (2 BOTTLES OF BEER) + (IF ONE OF THOSE BOTTLES SHOULD HAPPEN TO FALL) + (1 BOTTLES OF BEER ON THE WALL) + () + (1 BOTTLES OF BEER ON THE WALL) + (1 BOTTLES OF BEER) + (IF ONE OF THOSE BOTTLES SHOULD HAPPEN TO FALL) + (0 BOTTLES OF BEER ON THE WALL) + ()) +</PRE> + +<P>The problem is that we don't want a list. All we +want is to print out the lines of the song; storing them in a data structure +is unnecessary and inefficient. Also, some versions of Scheme would print +the above list like this: + +<P><PRE>((3 BOTTLES OF BEER ON THE WALL) (3 BOTTLES OF BEER) (IF ONE OF + THOSE BOTTLES SHOULD HAPPEN TO FALL) (2 BOTTLES OF BEER ON THE + WALL) () (2 BOTTLES OF BEER ON THE WALL) (2 BOTTLES OF BEER) (IF + ONE OF THOSE BOTTLES SHOULD HAPPEN TO FALL) (1 BOTTLES OF BEER ON + THE WALL) () (1 BOTTLES OF BEER ON THE WALL) (1 BOTTLES OF BEER) + (IF ONE OF THOSE BOTTLES SHOULD HAPPEN TO FALL) (0 BOTTLES OF BEER + ON THE WALL) ()) +</PRE> + +<P>or even all on one line. We can't rely on Scheme's mechanism for +printing lists if we want to be sure of a particular arrangement on the +screen. + +<P>Instead we'll write a program to <EM>print</EM> a verse, rather +than return it in a list: +<A NAME="g6"></A> + +<P><PRE>(define (<A NAME="g7"></A>bottles n) + (if (= n 0) + 'burp + (begin (verse n) + (bottles (- n 1))))) + +(define (<A NAME="g8"></A>verse n) + (show (cons n '(bottles of beer on the wall))) + (show (cons n '(bottles of beer))) + (show '(if one of those bottles should happen to fall)) + (show (cons (- n 1) '(bottles of beer on the wall))) + (show '())) + +> (bottles 3) +(3 BOTTLES OF BEER ON THE WALL) +(3 BOTTLES OF BEER) +(IF ONE OF THOSE BOTTLES SHOULD HAPPEN TO FALL) +(2 BOTTLES OF BEER ON THE WALL) +() +(2 BOTTLES OF BEER ON THE WALL) +(2 BOTTLES OF BEER) +(IF ONE OF THOSE BOTTLES SHOULD HAPPEN TO FALL) +(1 BOTTLES OF BEER ON THE WALL) +() +(1 BOTTLES OF BEER ON THE WALL) +(1 BOTTLES OF BEER) +(IF ONE OF THOSE BOTTLES SHOULD HAPPEN TO FALL) +(0 BOTTLES OF BEER ON THE WALL) +() +BURP +</PRE> + +<P> +Notice that Scheme doesn't print an outer set of parentheses. +Each line was printed separately; there isn't one big list containing all of +them.<A NAME="text2" HREF="io.html#ft2">[2]</A> + +<P>Why was "burp" printed at the end? Just because we're printing things +explicitly doesn't mean that the read-eval-print loop stops functioning. We +typed the expression <CODE>(bottles 3)</CODE>. In the course of evaluating that +expression, Scheme printed several lines for us. But the <EM>value</EM> of +the expression was the word <CODE>burp</CODE>, because that's what <CODE>bottles</CODE> +returned. + +<P><H2>Side Effects and Sequencing</H2> + +<P>How does our program work? There are two new ideas here: <EM><A NAME="g9"></A><A NAME="g10"></A>side effects</EM> and <EM>sequencing.</EM> + +<P>Until now, whenever we've invoked a procedure, our only goal has been to get +a return value. The procedures we've used compute and return a value, and do +nothing else. <CODE>Show</CODE> is different. Although every Scheme procedure +returns a value, the Scheme language standard doesn't specify what +value the printing procedures should return.<A NAME="text3" HREF="io.html#ft3">[3]</A> Instead, we are interested in their side effects. In other words, +we invoke <CODE>show</CODE> because we want it to <EM>do</EM> something, namely, +print its argument on the screen. + +<P>What exactly do we mean by "side effect"? The kinds of procedures that +we've used before this chapter can compute values, invoke helper procedures, +provide arguments to the helper procedures, and return a value. There may be +a lot of activity going on within the procedure, but the procedure +affects the world outside of itself only by returning a value that some +other procedure might use. <CODE>Show</CODE> affects the world outside of itself +by putting something on the screen. After <CODE>show</CODE> has finished its +work, someone who looks at the screen can tell that <CODE>show</CODE> was +used.<A NAME="text4" HREF="io.html#ft4">[4]</A> + +<P> + +<P>Here's an example to illustrate the difference between values and effects: + +<P><PRE>(define (<A NAME="g11"></A>effect x) + (show x) + 'done) + +(define (<A NAME="g12"></A>value x) + x) + +> (effect '(oh! darling)) +(OH! DARLING) +DONE + +> (value '(oh! darling)) +(OH! DARLING) + +> (bf (effect '(oh! darling))) +(OH! DARLING) +ONE +> (bf (value '(oh! darling))) +(DARLING) + +> (define (<A NAME="g13"></A>lots-of-effect x) + (effect x) + (effect x) + (effect x)) + +> (define (<A NAME="g14"></A>lots-of-value x) + (value x) + (value x) + (value x)) + +> (lots-of-effect '(oh! darling)) +(OH! DARLING) +(OH! DARLING) +(OH! DARLING) +DONE + +> (lots-of-value '(oh! darling)) +(OH! DARLING) +</PRE> + +<P> +This example also demonstrates the second new idea, +sequencing: Each of <CODE>effect</CODE>, <CODE>lots-of-effect</CODE>, +and <CODE>lots-of-value</CODE> contains more than one expression in +its body. When you invoke such a procedure, Scheme evaluates all +the expressions in the body, in order, and returns the value of the +last one.<A NAME="text5" HREF="io.html#ft5">[5]</A> This also +works in the body of a <CODE>let</CODE>, which is really the body of a +procedure, and in each clause of a <A NAME="g15"></A><CODE>cond</CODE>.<A NAME="text6" HREF="io.html#ft6">[6]</A> + +<P>When we invoked <CODE>lots-of-value</CODE>, Scheme invoked <CODE>value</CODE> three times; +it discarded the values returned by the first two invocations, and returned +the value from the third invocation. Similarly, when we invoked <CODE>lots-of-effect</CODE>, Scheme invoked <CODE>effect</CODE> three times and returned the +value from the third invocation. But each invocation of <CODE>effect</CODE> caused +its argument to be printed by invoking <CODE>show</CODE>. + +<P><H2>The <CODE><B>Begin</B></CODE> Special Form</H2> +<A NAME="beg"></A> + +<P>The <CODE>lots-of-effect</CODE> procedure accomplished sequencing by having more +than one expression in its body. This works fine if the sequence of events +that you want to perform is the entire body of a procedure. But in <CODE>bottles</CODE> we wanted to include a sequence as one of the alternatives in an +<CODE>if</CODE> construction. We couldn't just say + +<P><PRE>(define (bottles n) ;; wrong + (if (= n 0) + '() + (verse n) + (bottles (- n 1)))) +</PRE> + +<P>because <CODE>if</CODE> must have exactly three arguments. Otherwise, +how would <CODE>if</CODE> know whether we meant <CODE>(verse n)</CODE> to be the second +expression in the true case, or the first expression in the false case? + +<P>Instead, to turn the sequence of expressions into a single expression, we +use the <A NAME="g16"></A><A NAME="g17"></A>special form <A NAME="g18"></A><CODE>begin</CODE>. It takes any number of +arguments, evaluates them from left to right, and returns the value of the +last one. + +<P><PRE>(define bottles n) + (if (= n 0) + 'burp + (begin (verse n) + (bottles (- n 1))))) +</PRE> + +<P>(One way to think about sequences in procedure bodies is that +every procedure body has an invisible <CODE>begin</CODE> surrounding it.) + +<P><H2>This Isn't Functional Programming</H2> + +<P>Sequencing and side effects are radical departures from the idea of +<A NAME="g19"></A><A NAME="g20"></A>functional programming. In fact, we'd like to reserve the name <EM>function</EM> for something that computes and returns one value, with no side +effects. "Procedure" is the general term for the thing that <CODE>lambda</CODE> +returns—an embodiment of an algorithm. If the algorithm is the kind that +computes and returns a single value without side effects, then we say that +the procedure implements a function.<A NAME="text7" HREF="io.html#ft7">[7]</A> + +<P>There is a certain kind of sequencing even in functional programming. If +you say + +<P><PRE>(* (+ 3 4) (- 92 15)) +</PRE> + +<P>it's clear that the addition has to happen before the +multiplication, because the result of the addition provides one of the +arguments to the multiplication. What's new in the sequential programming +style is the <EM>emphasis</EM> on sequence, and the fact that the expressions +in the sequence are <EM>independent</EM> instead of contributing values to +each other. In this multiplication problem, for example, we don't care +whether the addition happens before or after the subtraction. If the +addition and subtraction were in a sequence, we'd be using them for +independent purposes: + +<P><PRE>(begin + (show (+ 3 4)) + (show (- 92 15))) +</PRE> + +<P>This is what we mean by being independent. Neither expression +helps in computing the other. And the order matters because we can see +the order in which the results are printed. + +<P><H2>Not Moving to the Next Line</H2> + +<P>Each invocation of <CODE>show</CODE> prints a separate line. What if we +want a program that prints several things on the same line, like this: + +<P><PRE>> (begin (show-addition 3 4) + (show-addition 6 8) + 'done) +3+4=7 +6+8=14 +DONE +</PRE> + +<P>We use <A NAME="g21"></A><CODE>display</CODE>, which doesn't move to the next line after +printing its argument: + +<P><PRE>(define (<A NAME="g22"></A>show-addition x y) + (display x) + (display '+) + (display y) + (display '=) + (show (+ x y))) +</PRE> + +<P>(The last one is a <CODE>show</CODE> because we <EM>do</EM> want to start +a new line after it.) + +<P>What if you just want to print a blank line? You use <A NAME="g23"></A><CODE>newline</CODE>: + +<P><PRE>(define (verse n) + (show (cons n '(bottles of beer on the wall))) + (show (cons n '(bottles of beer))) + (show '(if one of those bottles should happen to fall)) + (show (cons (- n 1) '(bottles of beer on the wall))) + (newline)) ; replaces (show '()) +</PRE> + +<P>In fact, <CODE>show</CODE> isn't an official Scheme primitive; we wrote it +in terms of <CODE>display</CODE> and <CODE>newline</CODE>. + +<P><H2>Strings</H2> + +<P>Throughout the book we've occasionally used strings, that is, words enclosed in +double-quote marks so that Scheme will permit the use of punctuation or other +unusual characters. Strings also preserve the case of letters, so they can +be used to beautify our song even more. Since <EM>any</EM> character can be +in a string, including spaces, the easiest thing to do in this case is +to treat all the letters, spaces, and punctuation characters of each line of +the song as one long word. (If we wanted to be able to compute functions of +the individual words in each line, that wouldn't be such a good idea.) + +<P><PRE>(define (<A NAME="g24"></A>verse n) + (display n) + (show " bottles of beer on the wall,") + (display n) + (show " bottles of beer.") + (show "If one of those bottles should happen to fall,") + (display (- n 1)) + (show " bottles of beer on the wall.") + (newline)) +</PRE> + +<P><PRE>> (verse 6) +6 bottles of beer on the wall, +6 bottles of beer. +If one of those bottles should happen to fall, +5 bottles of beer on the wall. + +#F ; or whatever is returned by (newline) +</PRE> + +<P> +It's strange to think of "<CODE> bottles of beer on the wall,</CODE>" as a single word. But the rule is that +anything inside double quotes counts as a single word. It doesn't have to +be an English word. + +<P><H2>A Higher-Order Procedure for Sequencing</H2> + +<P>Sometimes we want to print each element of a list separately: + +<P><PRE>(define (<A NAME="g25"></A>show-list lst) + (if (null? lst) + 'done + (begin (show (car lst)) + (show-list (cdr lst))))) + +> (show-list '((dig a pony) (doctor robert) (for you blue))) +(DIG A PONY) +(DOCTOR ROBERT) +(FOR YOU BLUE) +DONE +</PRE> + +<P>Like other patterns of computation involving lists, this one can be +abstracted into a higher-order procedure. (We can't call it a +"higher-order function" because this one is for computations with side +effects.) The procedure <CODE><A NAME="g26"></A><CODE>for-each</CODE></CODE> is part of standard Scheme: + +<P><PRE>> (for-each show '((mean mr mustard) (no reply) (tell me why))) +(MEAN MR MUSTARD) +(NO REPLY) +(TELL ME WHY) +</PRE> + +<P>The value returned by <CODE>for-each</CODE> is unspecified. + +<P>Why couldn't we just use <CODE>map</CODE> for this purpose? There are two reasons. +One is just an efficiency issue: <CODE>Map</CODE> constructs a list containing the +values returned by each of its sub-computations; in this example, it would +be a list of three instances of the unspecified value returned by <CODE>show</CODE>. But we aren't going to use that list for anything, so there's no +point in constructing it. The second reason is more serious. In functional +programming, the order of evaluation of subexpressions is unspecified. For +example, when we evaluate the expression + +<P><PRE>(- (+ 4 5) (* 6 7)) +</PRE> + +<P>we don't know whether the addition or the multiplication happens +first. Similarly, the order in which <CODE>map</CODE> computes the results for +each element is unspecified. That's okay as long as the ultimately returned +list of results is in the right order. But when we are using side effects, +we <EM>do</EM> care about the order of evaluation. In this case, we want +to make sure that the elements of the argument list are printed from left to +right. <CODE>For-each</CODE> guarantees this ordering. + +<P><H2>Tic-Tac-Toe Revisited</H2> + +<P>We're working up toward playing a game of tic-tac-toe against the computer. +But as a first step, let's have the computer play against itself. What we +already have is <CODE>ttt</CODE>, a <EM>strategy</EM> function: one that takes a +board position as argument (and also a letter <CODE>x</CODE> or <CODE>o</CODE>) and +returns the chosen next move. In order to play a game of tic-tac-toe, we +need two players; to make it more interesting, each should have its own +strategy. So we'll write another one, quickly, that just moves in the first +empty square it sees: + +<P><PRE>(define (<A NAME="g27"></A>stupid-ttt position letter) + (location '_ position)) + +(define (<A NAME="g28"></A>location letter word) + (if (equal? letter (first word)) + 1 + (+ 1 (location letter (bf word))))) +</PRE> + +<P>Now we can write a program that takes two strategies as arguments and +actually plays a game between them. + +<P><PRE>(define (<A NAME="g29"></A>play-ttt x-strat o-strat) + (play-ttt-helper x-strat o-strat '_________ 'x)) + +(define (<A NAME="g30"></A>play-ttt-helper x-strat o-strat position whose-turn) + (cond ((already-won? position (opponent whose-turn)) + (list (opponent whose-turn) 'wins!)) + ((tie-game? position) '(tie game)) + (else (let ((square (if (equal? whose-turn 'x) + (x-strat position 'x) + (o-strat position 'o)))) + (play-ttt-helper x-strat + o-strat + (add-move square whose-turn position) + (opponent whose-turn)))))) +</PRE> + +We use a helper procedure because we need to keep track of two +pieces of information besides the strategy procedures: the current board +position and whose turn it is (<CODE>x</CODE> or <CODE>o</CODE>). The helper procedure +is invoked recursively for each move. First it checks whether the game +is already over (won or tied).<A NAME="text8" HREF="io.html#ft8">[8]</A> +If not, the helper procedure invokes the current player's strategy procedure, +which returns the square number for the next move. For the recursive call, +the arguments are the same two strategies, the new position after the move, +and the letter for the other player. + +<P>We still need <CODE>add-move</CODE>, the procedure that takes a square and an old +position as arguments and returns the new position. + +<P><PRE>(define (<A NAME="g33"></A>add-move square letter position) + (if (= square 1) + (word letter (bf position)) + (word (first position) + (add-move (- square 1) letter (bf position))))) + +> (play-ttt ttt stupid-ttt) +(X WINS!) + +> (play-ttt stupid-ttt ttt) +(O WINS!) +</PRE> + +<P><H2>Accepting User Input</H2> + +<P>The work we did in the last section was purely functional. We didn't print +anything (except the ultimate return value, as always) and we didn't +have to read information from a human player, because there wasn't one. + +<P>You might expect that the structure of an <EM>interactive</EM> game program +would be very different, with a top-level procedure full of sequential +operations. But the fact is that we hardly have to change anything to turn +this into an interactive game. All we need is a new "strategy" procedure +that asks the user where to move, instead of computing a move based on +built-in rules. + +<P><PRE>(define (<A NAME="g34"></A>ask-user position letter) + (print-position position) + (display letter) + (display "'s move: ") + (read)) + +(define (print-position position) ;; first version + (show position)) +</PRE> + +<P>(Ultimately we're going to want a beautiful two-dimensional +display of the current position, but we don't want to get distracted by that +just now. That's why we've written a trivial temporary version.) + +<P><PRE>> (play-ttt ttt ask-user) +____X____ +O'S MOVE: <B>1</B> +O___XX___ +O'S MOVE: <B>4</B> +O__OXXX__ +O'S MOVE: <B>3</B> +OXOOXXX__ +O'S MOVE: <B>8</B> +(TIE GAME) +</PRE> + +<P>What the user typed is just the single digits shown in boldface at +the ends of the lines. + +<P>What's new here is that we invoke the procedure <A NAME="g35"></A><CODE>read</CODE>. It waits for +you to type a Scheme expression, and returns that expression. Don't +be confused: <CODE>Read</CODE> does <EM>not</EM> evaluate what you type. It +returns exactly the same expression that you type: + +<P><PRE>(define (<A NAME="g36"></A>echo) + (display "What? ") + (let ((expr (read))) + (if (equal? expr 'stop) + 'okay + (begin + (show expr) + (echo))))) +</PRE> + +<P><PRE>> (echo) +What? <B>hello</B> +HELLO +What? <B>(+ 2 3)</B> +(+ 2 3) +What? <B>(first (glass onion))</B> +(FIRST (GLASS ONION)) +What? <B>stop</B> +OKAY +</PRE> + +<P><H2>Aesthetic Board Display</H2> + +<P>Here's our beautiful position printer: + +<P> +<PRE>(define (<A NAME="g37"></A>print-position position) + (print-row (subword position 1 3)) + (show "-+-+-") + (print-row (subword position 4 6)) + (show "-+-+-") + (print-row (subword position 7 9)) + (newline)) + +(define (<A NAME="g38"></A>print-row row) + (maybe-display (first row)) + (display "|") + (maybe-display (first (bf row))) + (display "|") + (maybe-display (last row)) + (newline)) + +(define (<A NAME="g39"></A>maybe-display letter) + (if (not (equal? letter '_)) + (display letter) + (display " "))) + +(define (<A NAME="g40"></A>subword wd start end) + ((repeated bf (- start 1)) + ((repeated bl (- (count wd) end)) + wd)))<A NAME="text9" HREF="io.html#ft9">[9]</A></PRE> + + +Here's how it works: + +<P><PRE>> (print-position '_x_oo__xx) + |X| +-+-+- +O|O| +-+-+- + |X|X +</PRE> + +<H2>Reading and Writing Normal Text</H2> + +<P>The <CODE>read</CODE> procedure works fine as long as what you type looks like a +Lisp program. That is, it reads one expression at a time. In the +tic-tac-toe program the user types a single number, which is a Scheme +expression, so <CODE>read</CODE> works fine. But what if we want to read more than +one word? + +<P><PRE>(define (music-critic) ;; first version + (show "What's your favorite Beatles song?") + (let ((song (read))) + (show (se "I like" song "too.")))) + +> (music-critic) +What's your favorite Beatles song? +<B>She Loves You</B> +(I like SHE too.) +</PRE> + +<P>If the user had typed the song title in parentheses, then it would +have been a single Scheme expression and <CODE>read</CODE> would have accepted it. +But we don't want the users of our program to have to be typing parentheses +all the time. + +<P>Scheme also lets you read one character at a time. This allows you to read +any text, with no constraints on its format. The disadvantage is that you +find yourself putting a lot of effort into minor details. We've provided a +procedure <CODE><A NAME="g41"></A><CODE>read-line</CODE></CODE> that reads one line of input and returns a +sentence. The words in that sentence will contain any punctuation +characters that appear on the line, including parentheses, which are not +interpreted as sublist delimiters by <CODE>read-line</CODE>. <CODE>Read-line</CODE> also +preserves the case of letters. + +<P><PRE>(define (music-critic) ;; second version + (read-line) ; See explanation below. + (show "What's your favorite Beatles song?") + (let ((song (read-line))) + (show (se "I like" song "too.")))) + +> (music-critic) +What's your favorite Beatles song? +<B>She Loves You</B> +(I like She Loves You too.) +</PRE> + +<P>Why do we call <CODE>read-line</CODE> and ignore its result at the +beginning of <CODE>music-critic</CODE>? It has to do with the interaction between +<CODE>read-line</CODE> and <CODE>read</CODE>. <CODE>Read</CODE> treats what you type as a +sequence of Scheme expressions; each invocation of <CODE>read</CODE> reads one of +them. <CODE>Read</CODE> pays no attention to formatting details, such as several +consecutive spaces or line breaks. If, for example, you type several +expressions on the same line, it will take several invocations of <CODE>read</CODE> +to read them all. + +<P>By contrast, <CODE>read-line</CODE> treats what you type as a sequence of lines, +reading one line per invocation, so it does pay attention to line breaks. + +<P>Either of these ways to read input is sensible in itself, but if you mix +the two, by invoking <CODE>read</CODE> sometimes and <CODE>read-line</CODE> sometimes in +the same program, the results can be confusing. Suppose you type a line +containing an expression and your program invokes <CODE>read</CODE> to read it. +Since there might have been another expression on the line, <CODE>read</CODE> +doesn't advance to the next line until you ask for the next +expression. So if you now invoke <CODE>read-line</CODE>, thinking that it will +read another line from the keyboard, it will instead return an empty list, +because what it sees is an empty line—what's left after <CODE>read</CODE> uses up +the expression you typed. + +<P>You may be thinking, "But <CODE>music-critic</CODE> doesn't call <CODE>read</CODE>!" +That's true, but Scheme itself used <CODE>read</CODE> to read the expression that +you used to invoke <CODE>music-critic</CODE>. So the first invocation of <CODE>read-line</CODE> is needed to skip over the spurious empty line. + +<P>Our solution works only if <CODE>music-critic</CODE> is invoked directly at a +Scheme prompt. If <CODE>music-critic</CODE> were a subprocedure of some larger +program that has already called <CODE>read-line</CODE> before calling <CODE>music-critic</CODE>, the extra <CODE>read-line</CODE> in <CODE>music-critic</CODE> would really +read and ignore a useful line of text. + +<P>If you write a procedure using <CODE>read-line</CODE> that will sometimes be called +directly and sometimes be used as a subprocedure, you can't include an extra +<CODE>read-line</CODE> call in it. Instead, when you call your procedure directly +from the Scheme prompt, you must say + +<P><PRE>> (begin (read-line) (my-procedure)) +</PRE> + +<P>Another technical detail about <CODE>read-line</CODE> is that since +it preserves the capitalization of words, its result may +include strings, which will be shown in quotation marks if you return the +value rather than <CODE>show</CODE>ing it: + +<P><PRE>(define (music-critic-return) + (read-line) + (show "What's your favorite Beatles song?") + (let ((song (read-line))) + (se "I like" song "too."))) + +> (music-critic-return) +What's your favorite Beatles song? +<B>She Loves You</B> +("I like" "She" "Loves" "You" "too.") +</PRE> + +<P>We have also provided <CODE><A NAME="g42"></A><CODE>show-line</CODE>,</CODE> which takes a sentence +as argument. It prints the sentence without surrounding parentheses, +followed by a newline. (Actually, it takes any list as argument; it prints +all the parentheses except for the outer ones.) + +<P><PRE>(define (<A NAME="g43"></A>music-critic) + (read-line) + (show "What's your favorite Beatles song?") + (let ((song (read-line))) + (show-line (se "I like" song "too.")))) + +> (music-critic) +What's your favorite Beatles song? +<B>She Loves You</B> +I like She Loves You too. +</PRE> + +<P>The difference between <CODE>show</CODE> and <CODE>show-line</CODE> isn't +crucial. It's just a matter of a pair of parentheses. The point is that +<CODE>read-line</CODE> and <CODE>show-line</CODE> go together. <CODE>Read-line</CODE> reads a +bunch of disconnected words and combines them into a sentence. <CODE>Show-line</CODE> takes a sentence and prints it as if it were a bunch of +disconnected words. Later, when we read and write files in Chapter +22, this ability to print in the same form in which we read will be +important. + +<P><H2>Formatted Text</H2> + +<P>We've been concentrating on the use of sequential programming with explicit +<A NAME="spformat"></A> +printing instructions for the sake of conversational programs. Another +common application of sequential printing is to display tabular information, +such as columns of numbers. The difficulty is to get the numbers to line up +so that corresponding digits are in the same position, even when the numbers +have very widely separated values. The +<A NAME="g44"></A><CODE>align</CODE> function can be used to convert a number to a printable word +with a fixed number of positions before and after the decimal point: + +<P><PRE>(define (square-root-table nums) + (if (null? nums) + 'done + (begin (display (align (car nums) 7 1)) + (show (align (sqrt (car nums)) 10 5)) + (square-root-table (cdr nums))))) + +> (square-root-table '(7 8 9 10 20 98 99 100 101 1234 56789)) + 7.0 2.64575 + 8.0 2.82843 + 9.0 3.00000 + 10.0 3.16228 + 20.0 4.47214 + 98.0 9.89949 + 99.0 9.94987 + 100.0 10.00000 + 101.0 10.04988 + 1234.0 35.12834 +56789.0 238.30443 +DONE +</PRE> + +<P><CODE>Align</CODE> takes three arguments. The first is the value to be +displayed. The second is the width of the column in which it will be +displayed; the returned value will be a word with that many characters in it. +The third argument is the number of digits that should be displayed to the +right of the decimal point. (If this number is zero, then no decimal point +will be displayed.) The width must be great enough to include all the +digits, as well as the decimal point and minus sign, if any. + +<P>As the program example above indicates, <CODE>align</CODE> does not print +anything. It's a function that returns a value suitable for printing with +<CODE>display</CODE> or <CODE>show</CODE>. + +<P>What if the number is too big to fit in the available space? + +<P><PRE>> (align 12345679 4 0) +"123+" +</PRE> + +<P><CODE>Align</CODE> returns a word containing the first few digits, +as many as fit, ending with a plus sign to indicate that part of the value +is missing. + +<P><CODE>Align</CODE> can also be used to include non-numeric text in columns. If +the first argument is not a number, then only two arguments are needed; the +second is the column width. In this case <CODE>align</CODE> returns a word with +extra spaces at the right, if necessary, so that the argument word will +appear at the left in its column: + +<P><PRE>(define (<A NAME="g45"></A>name-table names) + (if (null? names) + 'done + (begin (display (align (cadar names) 11)) + (show (caar names)) + (name-table (cdr names))))) + +> (name-table '((john lennon) (paul mccartney) + (george harrison) (ringo starr))) +LENNON JOHN +MCCARTNEY PAUL +HARRISON GEORGE +STARR RINGO +DONE +</PRE> + +<P>As with numbers, if a non-numeric word won't fit in the allowed +space, <CODE>align</CODE> returns a partial word ending with a plus sign. + +<P>This <CODE>align</CODE> function is not part of standard Scheme. Most programming +languages, including some versions of Scheme, offer much more elaborate +formatting capabilities with many alternate ways to represent both numbers +and general text. Our version is a minimal capability to show the flavor +and to meet the needs of projects in this book. + +<P><H2>Sequential Programming and Order of Evaluation</H2> + +<P>Our expanded tic-tac-toe program includes both functional and sequential +parts. The program computes its strategy functionally but uses sequences +of commands to control the <EM><A NAME="g46"></A><A NAME="g47"></A>user interface</EM> by alternately +printing information to the screen and reading information from the keyboard. + +<P>By adding sequential programming to our toolkit, we've increased our ability +to write interactive programs. But there is a cost that goes along with +this benefit: We now have to pay more attention to the order of events than +we did in purely functional programs. + +<P>The obvious concern about order of events is that sequences of <CODE>show</CODE> +expressions must come in the order in which we want them to appear, and <CODE>read</CODE> expressions must fit into the sequence properly so that the user is +asked for the right information at the right time. + +<P>But there is another, less obvious issue about order of events. When the +evaluation of expressions can have side effects in addition to returning +values, the order of evaluation of argument subexpressions becomes important. +Here's an example to show what we mean. Suppose we type the expression + +<P><PRE>(list (+ 3 4) (- 10 2)) +</PRE> + +<P>The answer, of course, is <CODE>(7 8)</CODE>. It doesn't matter whether +Scheme computes the seven first (left to right) or the eight first (right to +left). But here's a similar example in which it <EM>does</EM> matter: + +<P><PRE>(define (<A NAME="g48"></A>show-and-return x) + (show x) + x) + +> (list (show-and-return (+ 3 4)) (show-and-return (- 10 2))) +8 +7 +(7 8) +</PRE> + +<P>The value that's ultimately returned, in this example, is the same +as before. But the two numeric values that go into the list are also +printed separately, so we can see which is computed first. (We've shown +the case of right-to-left computation; your Scheme might be different.) + +<P>Suppose you want to make sure that the seven prints first, regardless of +which order your Scheme uses. You could do this: + +<P><PRE>> (let ((left (show-and-return (+ 3 4)))) + (list left (show-and-return (- 10 2)))) +7 +8 +(7 8) +</PRE> + +<P>The expression in the body of a <CODE>let</CODE> can't be evaluated until +the <CODE>let</CODE> variables (such as <CODE>left</CODE>) have had their values computed. + +<P>It's hard to imagine a practical use for the artificial <CODE>show-and-return</CODE> procedure, but a similar situation arises whenever we use +<CODE>read</CODE>. Suppose we want to write a procedure to ask a person for his or +her full name, returning a two-element list containing the first and last +name. A natural mistake to make would be to write this procedure: + +<P><PRE>(define (ask-for-name) ;; wrong + (show "Please type your first name, then your last name:") + (list (read) (read))) + +> (ask-for-name) +Please type your first name, then your last name: +<B>John +Lennon</B> +(LENNON JOHN) +</PRE> + +<P>What went wrong? We happen to be using a version of Scheme that +evaluates argument subexpressions from right to left. Therefore, the word +<CODE>John</CODE> was read by the rightmost call to <CODE>read</CODE>, which provided the +second argument to <CODE>list</CODE>. The best solution is to use <CODE>let</CODE> as we +did above: + +<P><PRE>(define (<A NAME="g49"></A>ask-for-name) + (show "Please type your first name, then your last name:") + (let ((first-name (read))) + (list first-name (read)))) +</PRE> + +<P>Even this example looks artificially simple, because of the two invocations +of <CODE>read</CODE> that are visibly right next to each other in the erroneous +version. But look at <CODE>play-ttt-helper</CODE>. The word <CODE>read</CODE> doesn't +appear in its body at all. But when we invoke it using <CODE>ask-user</CODE> as +the strategy procedure for <CODE>x</CODE>, the expression + +<P><PRE>(x-strat position 'x) +</PRE> + +<P>hides an invocation of <CODE>read</CODE>. The structure of <CODE>play-ttt-helper</CODE> includes a <CODE>let</CODE> that controls the timing of that <CODE>read</CODE>. (As it turns out, in this particular case we could have gotten away +with writing the program without <CODE>let</CODE>. The hidden invocation of <CODE>read</CODE> is the only subexpression with a side effect, so there aren't two +effects that might get out of order. But we had to think carefully about +the program to be sure of that.) + +<P><H2>Pitfalls</H2> + +<P>It's easy to get confused about what is printed explicitly by your +<A NAME="g50"></A> +program and what is printed by Scheme's read-eval-print loop. Until now, +<EM>all</EM> printing was of the second kind. Here's an example that doesn't +do anything very interesting but will help make the point clear: + +<P><PRE>(define (name) + (display "MATT ") + 'wright) + +> (name) +MATT WRIGHT +</PRE> + +<P>At first glance it looks as if putting the word "Matt" inside a +call to <CODE>display</CODE> is unnecessary. After all, the word <CODE>wright</CODE> is +printed even without using <CODE>display</CODE>. But watch this: + +<P><PRE>> (bf (name)) +MATT RIGHT +</PRE> + +<P>Every time you invoke <CODE>name</CODE>, whether or not as the entire +expression used at a Scheme prompt, the word <CODE>MATT</CODE> is printed. But +the word <CODE>wright</CODE> is <EM>returned,</EM> and may or may not be printed +depending on the context in which <CODE>name</CODE> is invoked. + +<P>A sequence of expressions returns the value of the <EM>last</EM> +expression. If that isn't what you want, you must remember the value you +want to return using <CODE>let</CODE>: + +<P><PRE>(let ((result (compute-this-first))) + (begin + (compute-this-second) + (compute-this-third) + result)) +</PRE> + +<P>Don't forget that the first call to <CODE>read-line</CODE>, or any call to +<CODE>read-line</CODE> after a call to <CODE>read</CODE>, will probably read the empty +line that <CODE>read</CODE> left behind. + +<P>Sometimes you want to use what the user typed more than once in your +program. But don't forget that <CODE>read</CODE> has an effect as well as a return +value. Don't try to read the same expression twice: + +<P><PRE>(define (ask-question question) ;; wrong + (show question) + (cond ((equal? (read) 'yes) #t) + ((equal? (read) 'no) #f) + (else (show "Please answer yes or no.") + (ask-question question)))) +</PRE> + +<P>If the answer is <CODE>yes</CODE>, this procedure will work fine. But if +not, the second invocation of <CODE>read</CODE> will read a second expression, not +test the same expression again as intended. To avoid this problem, invoke +<CODE>read</CODE> only once for each expression you want to read, and use <CODE>let</CODE> +to remember the result: + +<P><PRE>(define (<A NAME="g51"></A>ask-question question) + (show question) + (let ((answer (read))) + (cond ((equal? answer 'yes) #t) + ((equal? answer 'no) #f) + (else (show "Please answer yes or no.") + (ask-question question))))) +</PRE> + +<P><H2>Boring Exercises</H2> + +<P><B>20.1</B> What happens when we evaluate the following expression? What is printed, +and what is the return value? Try to figure it out in your head before you +try it on the computer. + +<P><PRE>(cond ((= 2 3) (show '(lady madonna)) '(i call your name)) + ((< 2 3) (show '(the night before)) '(hello little girl)) + (else '(p.s. i love you))) +</PRE> + +<P> +<B>20.2</B> What does <CODE>newline</CODE> return in your version of Scheme? + + +<P><B>20.3</B> Define <CODE>show</CODE> in terms of <CODE>newline</CODE> and <CODE>display</CODE>. + + +<P> +<H2>Real Exercises</H2> + +<P><B>20.4</B> Write a program that carries on a conversation like the following example. +What the user types is in boldface. + +<P><PRE>> <B>(<A NAME="g52"></A>converse)</B> +Hello, I'm the computer. What's your name? <B>Brian Harvey</B> +Hi, Brian. How are you? <B>I'm fine.</B> +Glad to hear it. +</PRE> + +<P> +<B>20.5</B> Our <CODE>name-table</CODE> procedure uses a fixed width for the column containing +the last names of the people in the argument list. Suppose that instead of +liking British-invasion music you are into late romantic Russian composers: + +<P><PRE>> (name-table '((piotr tchaikovsky) (nicolay rimsky-korsakov) + (sergei rachmaninov) (modest musorgsky))) +</PRE> + +<P>Alternatively, perhaps you like jazz: + +<P><PRE>> (name-table '((bill evans) (paul motian) (scott lefaro))) +</PRE> + +<P>Modify <CODE>name-table</CODE> so that it figures out the longest last +name in its argument list, adds two for spaces, and uses that number as the +width of the first column. + + +<P> +<B>20.6</B> The procedure <CODE>ask-user</CODE> isn't robust. What happens if you type +something that isn't a number, or isn't between 1 and 9? Modify it to check +that what the user types is a number between 1 and 9. If not, it should +print a message and ask the user to try again. + + +<P> +<B>20.7</B> Another problem with <CODE>ask-user</CODE> is that it allows a user to request a +square that isn't free. If the user does this, what happens? Fix <CODE>ask-user</CODE> to ensure that this can't happen. + + +<P> +<B>20.8</B> At the end of the game, if the computer wins or ties, you never find out +which square it chose for its final move. Modify the program to correct +this. (Notice that this exercise requires you to make <CODE>play-ttt-helper</CODE> +non-functional.) + + +<P> +<B>20.9</B> The way we invoke the game program isn't very user-friendly. Write a +procedure <CODE>game</CODE> that asks you whether you wish to play <CODE>x</CODE> or <CODE>o</CODE>, then starts a game. (By definition, <CODE>x</CODE> plays first.) Then write a +procedure <CODE>games</CODE> that allows you to keep playing repeatedly. It +can ask "do you want to play again?" after each game. (Make sure that +the outcome of each game is still reported, and that the user can choose +whether to play <CODE>x</CODE> or <CODE>o</CODE> before each game.) + + +<P> + +<HR> +<A NAME="ft1" HREF="io.html#text1">[1]</A> The only +exception is that we've used <CODE>trace</CODE>, which prints messages about +the progress of a computation.<P> +<A NAME="ft2" HREF="io.html#text2">[2]</A> We know that it's still not as beautiful as can be, because of +the capital letters and parentheses, but we'll get to that later.<P> +<A NAME="ft3" HREF="io.html#text3">[3]</A> Suppose <CODE>show</CODE> +returns <CODE>#f</CODE> in your version of Scheme. Then you might see + +<P><PRE>> (show 7) +7 +#F +</PRE> + +<P>But since the return value is unspecified, we try to write +programs in such a way that we never use <CODE>show</CODE>'s return value as the +return value from our procedures. That's why we return values like <CODE>burp</CODE>.<P> +<A NAME="ft4" HREF="io.html#text4">[4]</A> The term <EM>side</EM> effect is based on the idea that a +procedure may have a useful return value as its main purpose and may also +have an effect "on the side." It's a misnomer to talk about the +side effect of <CODE>show</CODE>, since the effect is its main purpose. But nobody +ever says "side return value"!<P> +<A NAME="ft5" HREF="io.html#text5">[5]</A> In Chapter 4, we said that the body of a +procedure was always one single expression. We lied. But as long +as you don't use any procedures with side effects, it doesn't do you +any good to evaluate more than one expression in a body.<P> +<A NAME="ft6" HREF="io.html#text6">[6]</A> For example: + +<P><PRE>> (cond ((< 4 0) + (show '(how interesting)) + (show '(4 is less than zero?)) + #f) + ((> 4 0) + (show '(more reasonable)) + (show '(4 really is more than zero)) + 'value) + (else + (show '(you mean 4=0?)) + #f)) +(MORE REASONABLE) +(4 REALLY IS MORE THAN ZERO) +VALUE</PRE><P> +<A NAME="ft7" HREF="io.html#text7">[7]</A> Sometimes people sloppily +say that the procedure <EM>is</EM> a function. In fact, you may hear people +be <EM>really</EM> sloppy and call a non-functional procedure a function!<P> +<A NAME="ft8" HREF="io.html#text8">[8]</A> You wrote the procedures <CODE>already-won?</CODE> and <CODE>tie-game?</CODE> in Exercises <A HREF="../ssch10/ttt.html#tttwon">10.1</A> and <A HREF="../ssch10/ttt.html#ttttied">10.2</A>: + +<P><PRE>(define (<A NAME="g31"></A>already-won? position who) + (member? (word who who who) (find-triples position))) + +(define (<A NAME="g32"></A>tie-game? position) + (not (member? '_ position))) +</PRE><P> +<A NAME="ft9" HREF="io.html#text9">[9]</A> Alternate version: + +<PRE>(define (subword wd start end) + (cond ((> start 1) (subword (bf wd) (- start 1) (- end 1))) + ((< end (count wd)) (subword (bl wd) start end)) + (else wd))) +</PRE> + +<P>You can take your choice, depending on which you think is easier, +recursion or higher-order functions. +<P> +<P><A HREF="../ss-toc2.html">(back to Table of Contents)</A><P> +<A HREF="part6.html"><STRONG>BACK</STRONG></A> +chapter thread <A HREF="../ssch21/functions-implement.html"><STRONG>NEXT</STRONG></A> + +<P> +<ADDRESS> +<A HREF="../index.html">Brian Harvey</A>, +<CODE>bh@cs.berkeley.edu</CODE> +</ADDRESS> +</BODY> +</HTML> diff --git a/js/games/nluqo.github.io/~bh/ssch20/part6.html b/js/games/nluqo.github.io/~bh/ssch20/part6.html new file mode 100644 index 0000000..ac9eabe --- /dev/null +++ b/js/games/nluqo.github.io/~bh/ssch20/part6.html @@ -0,0 +1,111 @@ +<P> + +<P> +<HTML> +<HEAD> +<TITLE>Simply Scheme: Introducing Computer Science, Part 20: Sequential Programming</TITLE> +</HEAD> +<BODY> +<CITE>Simply Scheme:</CITE> +<CITE>Introducing Computer Science</CITE> 2/e Copyright (C) 1999 MIT +<H2>Part VI</H2> +<H1>Sequential Programming</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/ssch20.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="../ssch19/implement-hof.html"><STRONG>BACK</STRONG></A> +chapter thread <A HREF="io.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><BIG> + +<P>The three big ideas in this part are <EM>effect, sequence,</EM> and <EM>state.</EM> + +<P>Until now, we've been doing functional programming, where the focus is on +functions and their return values. Invoking a function is like asking a +question: "What's two plus two?" In this part of the book we're going to +talk about giving commands to the computer as well as asking it questions. +That is, we'll invoke procedures that tell Scheme to <EM>do</EM> something, +such as <CODE>wash-the-dishes</CODE>. (Unfortunately, the Scheme standard +leaves out this primitive.) Instead of merely computing a value, such a +procedure has an <EM>effect,</EM> an action that changes something. + +<P>Once we're thinking about actions, it's very natural to consider a <EM>sequence</EM> of actions. First cooking dinner, then eating, and then washing +the dishes is one sequence. First eating, then washing the dishes, and then +cooking is a much less sensible sequence. + +<P>Although these ideas of sequence and effect are coming near the end of our +book, they're the ideas with which almost every introduction to programming +begins. Most books compare a program to a recipe or a sequence of +instructions, along the lines of + +<P><PRE>to go-to-work + get-dressed + eat-breakfast + catch-the-bus +</PRE> + +<P>This sequential programming style is simple and natural, and it does +a good job of modeling computations in which the problem concerns +a sequence of events. If you're writing an airline reservation system, a +sequential program with <CODE>reserve-seat</CODE> and <CODE>issue-ticket</CODE> commands +makes sense. But if you want to know the acronym of a phrase, that's +not inherently sequential, and a question-asking approach is best. + +<P>Some actions that Scheme can take affect the "outside" world, such as +printing something on the computer screen. But Scheme can also carry out +internal actions, invisible outside the computer, but changing the +environment in which Scheme itself carries out computations. Defining a new +variable with <CODE>define</CODE> is an example; before the definition, Scheme +wouldn't understand what that name means, but once the definition has been +made, the name can be used in evaluating later expressions. Scheme's +knowledge about the leftover effects of past computations is called its <EM>state.</EM> The third big idea in this part of the book is that we can write +programs that maintain state information and use it to determine their +results. + +<P>Like sequence, the notion of state contradicts functional programming. +Earlier in the book, we emphasized that every time a function is invoked +with the same arguments, it must return the same value. But a procedure +whose returned value depends on state—on the past history of the +computation—might return a different value on each invocation, even with +identical arguments. + +<P>We'll explore several situations in which effects, +sequence, and state are useful: + +<P><P><TABLE><TR><TH align="right" valign="top">•<TD> <TD valign="top">Interactive, question-and-answer programs that involve keyboard input +while the computation is in progress; +</TABLE><TABLE><TR><TH align="right" valign="top">•<TD> <TD valign="top">Programs that must read and write long-term data file storage; +</TABLE><TABLE><TR><TH align="right" valign="top">•<TD> <TD valign="top">Computations that <EM>model</EM> an actual sequence of events in time +and use the state of the program to model information about the state of +the simulated events. + +</TABLE><P> +After introducing Scheme's mechanisms for sequential programming, we'll use +those mechanisms to implement versions of two commonly used types of +business computer applications, a spreadsheet and a database program. + +<P> +</BIG> +<HR> +<P><A HREF="../ss-toc2.html">(back to Table of Contents)</A><P> +<A HREF="../ssch19/implement-hof.html"><STRONG>BACK</STRONG></A> +chapter thread <A HREF="io.html"><STRONG>NEXT</STRONG></A> + +<P> +<ADDRESS> +<A HREF="../index.html">Brian Harvey</A>, +<CODE>bh@cs.berkeley.edu</CODE> +</ADDRESS> +</BODY> +</HTML> |