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/v1ch4/v1ch4.html | |
parent | 5d012c6c011a9dedf7d0a098e456206244eb5a0f (diff) | |
download | tour-562a9a52d599d9a05f871404050968a5fd282640.tar.gz |
*
Diffstat (limited to 'js/games/nluqo.github.io/~bh/v1ch4/v1ch4.html')
-rw-r--r-- | js/games/nluqo.github.io/~bh/v1ch4/v1ch4.html | 830 |
1 files changed, 830 insertions, 0 deletions
diff --git a/js/games/nluqo.github.io/~bh/v1ch4/v1ch4.html b/js/games/nluqo.github.io/~bh/v1ch4/v1ch4.html new file mode 100644 index 0000000..c36950f --- /dev/null +++ b/js/games/nluqo.github.io/~bh/v1ch4/v1ch4.html @@ -0,0 +1,830 @@ +<HTML> +<HEAD> +<TITLE>Computer Science Logo Style vol 1 ch 4: Predicates</TITLE> +</HEAD> +<BODY> +<CITE>Computer Science Logo Style</CITE> volume 1: +<CITE>Symbolic Computing</CITE> 2/e Copyright (C) 1997 MIT +<H1>Predicates</H1> + +<TABLE width="100%"><TR><TD> +<IMG SRC="../csls1.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"><BR> +<TR><TD align="right"><A HREF="../pdf/v1ch04.pdf">Download PDF version</A> +<TR><TD align="right"><A HREF="../v1-toc2.html">Back to Table of Contents</A> +<TR><TD align="right"><A HREF="../v1ch3/v1ch3.html"><STRONG>BACK</STRONG></A> +chapter thread <A HREF="../v1ch5/v1ch5.html"><STRONG>NEXT</STRONG></A> +<TR><TD align="right"><A HREF="https://mitpress.mit.edu/books/computer-science-logo-style-second-edition-volume-1">MIT +Press web page for Computer Science Logo Style</A> +</TABLE></TABLE> + +<HR> + +<P> +By introducing variables in Chapter 3, we made it possible for a +procedure to operate on different data each time you invoke it. But +the <EM>pattern</EM> of what the procedure does with the data remains +constant. We can get even more variety out of our procedures if we +can vary the <EM>instructions</EM> that the procedure executes. We +need a way to say, "Sometimes do this; other times do that." + +<P><H2>True or False</H2> + +<P>One helpful metaphor is this: When you invoke a command, you're giving +the computer an order. "Now hear this! <CODE>Print</CODE> such-and-such!" +But when you invoke an operation, you're asking the computer a <EM> +question.</EM> "What is the <CODE>first</CODE> member of such-and-such?" + +<P>In real life we single out as a special category <EM> +yes-or-no questions.</EM> +For example, these special questions form the basis of the +game Twenty Questions. The corresponding +category in Logo is the <EM>predicate.</EM> +A predicate is an operation whose output is always either the word +<CODE>true</CODE> or the word <CODE>false</CODE>. + +<P>For example, <CODE>listp</CODE> (pronounced "list-pea") is a predicate +that takes one input. The input can be any datum. The output from +<CODE>listp</CODE> is <CODE>true</CODE> if the input is a list, <CODE>false</CODE> if the +input is a word. + +<P><CODE>Wordp</CODE> is another predicate that takes one input. The input can +be any datum. The output from <CODE>wordp</CODE> is <CODE>true</CODE> if the input +is a word, <CODE>false</CODE> if the input is a list. (This is the opposite +of the output from <CODE>listp</CODE>.) + +<P><CODE>Emptyp</CODE> is also a predicate with one input. The input can be any +datum. The output from <CODE>emptyp</CODE> is <CODE>true</CODE> if the input is +either the empty word or the empty list; if the input is anything +else, the output is <CODE>false</CODE>. + +<P>You'll have noticed by now that predicates tend to have names ending +in the letter <CODE>p</CODE>. This is not quite a universal rule, but almost. +It's a good idea to follow the same convention in naming your +own predicates.<SUP>*</SUP> + +<P><SMALL><BLOCKQUOTE><SMALL><SUP>*</SUP>Many versions of Logo use a question mark at the end +of names of predicates, instead of a <CODE>p</CODE>. For example, you may see <CODE> +list?</CODE> instead of <CODE>listp</CODE>. Berkeley Logo accepts either form, but I +prefer the <CODE>p</CODE> version.</SMALL></BLOCKQUOTE></SMALL><P> + +<P>As I'm describing primitive predicates, you might want to try them +out on the computer. You can do experiments like this: + +<P><PRE>? <U>print wordp "hello</U> +true +? <U>print wordp [hello]</U> +false +? <U>print emptyp []</U> +true +? <U>print emptyp 0</U> +false +</PRE> + +<P>Of course, most of the time you won't actually want to print +the output from a predicate. You'll see in a few moments +how we can use a predicate to control the instructions carried out +in a procedure. + +<P>But first here are a few more primitive predicates. <CODE>Numberp</CODE> takes one +input, which can be any datum. The output from <CODE>numberp</CODE> is <CODE> +true</CODE> if the input is a number, <CODE>false</CODE> otherwise. + +<P><CODE>Equalp</CODE> takes two inputs, each of which can be any datum. The output +from <CODE>equalp</CODE> is <CODE>true</CODE> if the two inputs are identical or if they're +both numbers and they're numerically equal. That is, 3 and 3.0 are +numerically equal even though they're not identical words. A list is +never equal to a word. + +<P><PRE>? <U>print equalp 3 3.0</U> +true +? <U>print equalp "hello [hello]</U> +false +? <U>print equalp "hello first [hello]</U> +true +? <U>print equalp " []</U> +false +? <U>print equalp [] butfirst [hello]</U> +true +</PRE> + +<P> + +<P>The equal sign (<CODE>=</CODE>) can +be used as an <EM>infix</EM> equivalent +of <CODE>equalp</CODE>: + +<P><PRE>? <U>print "hello = first [hello]</U> +true +? <U>print 2 = 3</U> +false +</PRE> + +<P>As I mentioned in Chapter 2, if you use infix operations +you have to be careful about what is grouped with what. It varies +between versions of Logo. Here is an example I tried in Berkeley Logo: + +<P><PRE>? <U>print first [hello] = "hello</U> +f +</PRE> + +<P>Among current commercial implementations, Object Logo and +Microworlds give the same answer <CODE>f</CODE>. But here is the <EM>same</EM> +example in Logowriter: + +<P><PRE>? <U>print first [hello] = "hello</U> +true +</PRE> + +<P>You can avoid confusion by using parentheses. The following +instructions work reliably in any Logo: + +<P><PRE>? <U>print (first [hello]) = "hello</U> +true +? <U>print first ([hello] = "hello)</U> +f +</PRE> + +<P><CODE>Memberp</CODE> is a predicate with two inputs. If the second input is a +list, then the first can be any datum. If the second input is a word, then +the first must be a one-character word. The output from <CODE>memberp</CODE> +is true if the first input is a member of the second input. + +<P><PRE>? <U>print memberp "rain [the rain in Spain]</U> +true +? <U>print memberp [the rain] [the rain in Spain]</U> +false +? <U>print memberp [the rain] [[the rain] in Spain]</U> +true +? <U>print memberp "e "please</U> +true +? <U>print memberp "e "plain</U> +false +</PRE> + +<P><CODE>Lessp</CODE> and <CODE>greaterp</CODE> are predicates that take two inputs. +Both inputs must be numbers. The output from <CODE>lessp</CODE> is <CODE>true</CODE> if +the first input is numerically less than the second; the output from <CODE> +greaterp</CODE> is true if the first is greater than the second. Otherwise the +output is <CODE>false</CODE>. (In particular, both <CODE>lessp</CODE> and <CODE>greaterp</CODE> +output <CODE>false</CODE> if the two inputs are equal.) The infix forms for <CODE> +lessp</CODE> (<CODE><</CODE>) and <CODE>greaterp</CODE> (<CODE>></CODE>) are also allowed. + +<P><H2>Defining Your Own Predicates</H2> + +<P>Here are two examples of how you can create new predicates: + +<P><PRE>to vowelp :letter +output memberp :letter [a e i o u] +end + +? <U>print vowelp "e</U> +true +? <U>print vowelp "g</U> +false + +to oddp :number +output equalp (remainder :number 2) 1 +end + +? <U>print oddp 5</U> +true +? <U>print oddp 8</U> +false +</PRE> + +<P><H2>Conditional Evaluation</H2> + +<P> The main use of predicates is to +compute inputs to the primitive procedures <CODE>if</CODE> and <CODE>ifelse</CODE>. +We'll get to <CODE>ifelse</CODE> in a while, but first we'll explore <CODE>if</CODE>. + +<P> +<CODE>If</CODE> is a command with two inputs. The first input must +be either the word <CODE>true</CODE> or the word <CODE>false</CODE>. The second input must +be a list containing Logo instructions. If the first input is <CODE>true</CODE>, +the effect of <CODE>if</CODE> is to evaluate the instructions in the second input. +If the first input is <CODE>false</CODE>, <CODE>if</CODE> has no effect. + +<P><PRE>? <U>if equalp 2 1+1 [print "Yup.]</U> +Yup. +? <U>if equalp 3 2 [print "Nope.]</U> +? +</PRE> + +<P>Here is an example of how <CODE>if</CODE> can be used in a procedure. +This is an extension of the <CODE>converse</CODE> example in Chapter 3: + +<P><PRE>to talk +local "name +print [Please type your full name.] +make "name readlist +print sentence [Your first name is] first :name +if (count :name) > 2 ~ + [print sentence [Your middle name is] first bf :name] +print sentence [Your last name is] last :name +end + +? <U>talk</U> +Please type your full name. +<U>George Washington</U> +Your first name is George +Your last name is Washington +? <U>talk</U> +Please type your full name. +<U>John Paul Jones</U> +Your first name is John +Your middle name is Paul +Your last name is Jones +</PRE> + +<P><CODE>Talk</CODE> asks you to type your name and reads what you type +into a list, which is remembered in the variable named <CODE> +name</CODE>. Your first and last names are printed as in the earlier +version. If the list <CODE>:name</CODE> contains more than two members, +however, <CODE>talk</CODE> also prints the second member as your middle +name. If <CODE>:name</CODE> contains only two members, <CODE>talk</CODE> assumes +that you don't have a middle name. + +<P>»Write a procedure of your own that asks a question and uses <CODE>if</CODE> to +find out something about the response. + +<P>You can use <CODE>if</CODE> to help in writing more interesting predicates. + +<P><PRE>to about.computersp :sentence +if memberp "computer :sentence [output "true] +if memberp "computers :sentence [output "true] +if memberp "programming :sentence [output "true] +output "false +end + +? <U>print about.computersp [This book is about programming]</U> +true +? <U>print about.computersp [I like ice cream]</U> +false +? +</PRE> + +<P>This procedure illustrates something I didn't explain before about <CODE> +output</CODE>: An <CODE>output</CODE> command finishes the evaluation of the +procedure in which it occurs. For example, in <CODE>about.computersp</CODE>, +if the input sentence contains the word <CODE>computer</CODE>, the first <CODE> +if</CODE> evaluates the <CODE>output</CODE> instruction that is its second input. +The procedure immediately outputs the word <CODE>true</CODE>. The remaining +instructions are not evaluated at all. + +<P>»Write <CODE>past.tensep</CODE>, which takes a word as input and +outputs <CODE>true</CODE> if the word ends in <CODE>ed</CODE> or if it's one of a list of +exceptions, like <CODE>saw</CODE> and <CODE>went</CODE>. + +<P>»Write <CODE>integerp</CODE>, which takes any Logo datum as input and +outputs <CODE>true</CODE> if and only if the datum is an integer (a number without a +fraction part). Hint: a number with a fraction part will contain a decimal +point. + +<P><H2>Choosing Between Alternatives</H2> + +<P><CODE>If</CODE> gives the choice between carrying out some instructions and doing +nothing at all. More generally, we may want to carry out either of <EM> +two</EM> sets of instructions, depending on the output from a predicate. +The primitive procedure <CODE>ifelse</CODE> meets this need.<SUP>*</SUP> <CODE>Ifelse</CODE> is +an unusual primitive because it can be used either as a command or as an +operation. We'll start with examples in which <CODE>ifelse</CODE> is used as a +command. + +<P><SMALL><BLOCKQUOTE><SMALL><SUP>*</SUP>In some +versions of Logo, the name <CODE>if</CODE> is used both for the two-input command +discussed earlier and for the three-input one presented here.</SMALL></BLOCKQUOTE></SMALL><P><CODE>Ifelse</CODE> requires three inputs. The first input must be either the word +<CODE>true</CODE> or the word <CODE>false</CODE>. The second and third inputs must be +lists containing Logo instructions. If the first input is <CODE>true</CODE>, the +effect of <CODE>if</CODE> is to evaluate the instructions in the second input. If +the first input is <CODE>false</CODE>, the effect is to evaluate the instructions +in the third input. + +<P><PRE>? <U>ifelse 4 = 2+2 [print "Yup.] [print "Nope.]</U> +Yup. +? <U>ifelse 4 = 3+5 [print "Yup.] [print "Nope.]</U> +Nope. +? +</PRE> + +<P> +Here is an example of a procedure using <CODE>ifelse</CODE>: + +<P><PRE>to groupie +local "name +print [Hi, who are you?] +make "name readlist +ifelse :name = [Ray Davies] ~ + [print [May I have your autograph?]] ~ + [print sentence "Hi, first :name] +end + +? <U>groupie</U> +Hi, who are you? +<U>Frank Sinatra</U> +Hi, Frank +? <U>groupie</U> +Hi, who are you? +<U>Ray Davies</U> +May I have your autograph? +</PRE> + +<P>»Write an operation <CODE>color</CODE> that takes as input a word +representing a card, such as <CODE>10h</CODE> for the ten of hearts. Its output +should be the word <CODE>red</CODE> if the card is a heart or a diamond, or <CODE> +black</CODE> if it's a spade or a club. + +<P>»Write a conversational program that asks the user's name and figures +out how to address him or her. For example: + +<P><PRE>? <U>converse</U> +Hi, what's your name? +<U>Chris White</U> +Pleased to meet you, Chris. + +? <U>converse</U> +Hi, what's your name? +<U>Ms. Grace Slick</U> +Pleased to meet you, Ms. Slick. + +? <U>converse</U> +Hi, what's your name? +<U>J. Paul Getty</U> +Pleased to meet you, Paul. + +? <U>converse</U> +Hi, what's your name? +<U>Sigmund Freud, M.D.</U> +Pleased to meet you, Dr. Freud. + +? <U>converse</U> +Hi, what's your name? +<U>Mr. Lon Chaney, Jr.</U> +Pleased to meet you, Mr. Chaney. +</PRE> + +<P>What should the program say if it meets Queen Elizabeth II? + + +<P><H2>Conditional Evaluation Another Way</H2> + +<P>The use of <CODE>ifelse</CODE> in the <CODE>groupie</CODE> example above makes for +a rather long instruction line. If you wanted to do several instructions +in each case, rather than just one <CODE>print</CODE>, the <CODE>if</CODE> line would become +impossible to read. Logo provides another mechanism that is equivalent +to the <CODE>ifelse</CODE> command but may be easier to read. + +<P><CODE>Test</CODE> is a command that takes one input. The input must be +either the word <CODE>true</CODE> or the word <CODE>false</CODE>. The effect of +<CODE>test</CODE> is just to remember what its input was in a special place. +You can think of this place as a variable without a name. This +special variable is automatically local to the procedure from which +<CODE>test</CODE> is invoked. + +<P><CODE>Iftrue</CODE> (abbreviation <CODE>ift</CODE>) is a command with one +input. The input must be a list of Logo instructions. The effect of +<CODE>iftrue</CODE> is to evaluate the instructions in its input only if the +unnamed variable set by the most recent <CODE>test</CODE> command in the same +procedure is <CODE>true</CODE>. It is an error to use <CODE>iftrue</CODE> without +first using <CODE>test</CODE>. + +<P><CODE>Iffalse</CODE> (abbreviation <CODE>iff</CODE>) is a command with one input, which +must be an instruction list. The effect of <CODE>iffalse</CODE> is to evaluate +the instructions only if the remembered result of the most recent <CODE>test</CODE> +command is <CODE>false</CODE>. + +<P><CODE>Iftrue</CODE> and <CODE>iffalse</CODE> can be invoked as many times as you like after +a <CODE>test</CODE>. This allows you to break up a long sequence of conditionally evaluated +instructions into several instruction lines: + +<P><PRE>to better.groupie +local "name +print [Hi, who are you?] +make "name readlist +test equalp :name [Ray Davies] +iftrue [print [Wow, can I have your autograph?]] +iftrue [print [And can I borrow a thousand dollars?]] +iffalse [print sentence [Oh, hello,] first :name] +end +</PRE> + +<P><H2>About Those Brackets</H2> + +<P>I hope that the problem I'm about to mention won't even have occurred +to you because you are so familiar with the idea of evaluation that +you understood right away. But you'll probably have to explain it +to someone else, so I thought I'd bring it up here: + +<P> + +Some people get confused about why the second +input to <CODE>if</CODE> (and the second and third inputs to <CODE>ifelse</CODE>) +is surrounded by brackets but the first isn't. That +is, they wonder, why don't we say + +<P><PRE>if [equalp 2 3] [print "really??] ; (wrong!) +</PRE> + +<P>They have this problem because someone lazily told them +to put brackets around the conditionally evaluated instructions without +ever explaining about brackets and quotation. + +<P>I trust <EM>you</EM> aren't confused that way. You understand that, as usual, +Logo evaluates the inputs to a procedure before invoking the procedure. The +first input to <CODE>if</CODE> has to be either the word <CODE>true</CODE> or the word +<CODE>false</CODE>. <EM>Before</EM> invoking <CODE>if</CODE>, Logo has to evaluate an +expression like <CODE>equalp 2 3</CODE> to compute the input. (In this case, the +result output by <CODE>equalp</CODE> will be <CODE>false</CODE>.) But if the <CODE>print</CODE> +instruction weren't quoted, Logo would evaluate it, too, <EM>before</EM> +invoking <CODE>if</CODE>. That's not what we want. We want the instruction list +<EM>itself</EM> to be the second input, so that <CODE>if</CODE> can decide whether +or not to carry out the instructions in the list. So, as usual, we use +brackets to tell Logo to quote the list. + +<P><CENTER><TABLE rules="groups" frame="void" border="2"> +<THEAD> +<TR><TH>actual argument expression<TH>--><TH>actual argument value +<TBODY> +<TR><TD><CODE>equalp 2 3</CODE><TD>--><TD><CODE>false</CODE> +<TR><TD><CODE>[print "really??]</CODE><TD>--><TD><CODE>[print "really??]</CODE> +</TABLE></CENTER> + +<P><H2>Logical Connectives</H2> + +<P>Sometimes the condition under which you want to evaluate an instruction +is complicated. You want to do it if <EM>both</EM> this <EM>and</EM> that +are true, or if <EM>either</EM> this <EM>or</EM> that is true. Logo provides +operations for this purpose. + +<P><CODE>And</CODE> is a predicate with two inputs. Each input must be either the +word <CODE>true</CODE> or the word <CODE>false</CODE>. The output from <CODE>and</CODE> is +<CODE>true</CODE> if both inputs are <CODE>true</CODE>; the output is <CODE>false</CODE> if +either input is <CODE>false</CODE>. (<CODE>And</CODE> can take more than two inputs if +the entire expression is enclosed in parentheses. In that case the output +from <CODE>and</CODE> will be <CODE>true</CODE> only if all of its inputs are <CODE>true</CODE>.) + +<P><CODE>Or</CODE> is a predicate with two inputs. Each input must be either the +word <CODE>true</CODE> or the word <CODE>false</CODE>. The output from <CODE>or</CODE> is +<CODE>true</CODE> if either input is <CODE>true</CODE> (or both inputs are). The +output is <CODE>false</CODE> if both inputs are <CODE>false</CODE>. (Extra-input +<CODE>or</CODE> outputs <CODE>true</CODE> if any of its inputs are <CODE>true</CODE>, <CODE> +false</CODE> if all inputs are <CODE>false</CODE>.) + +<P><CODE>Not</CODE> is a predicate with one input. The input must be either the +word <CODE>true</CODE> or the word <CODE>false</CODE>. The output from <CODE>not</CODE> is +the opposite of its input: <CODE>true</CODE> if the input is <CODE>false</CODE>, or +<CODE>false</CODE> if the input is <CODE>true</CODE>. + +<P> + +These three procedures are called <EM>logical connectives</EM> because +they connect logical expressions together into bigger ones. (A <EM> +logical</EM> expression is one whose value is <CODE>true</CODE> or <CODE> +false</CODE>.) They can be useful in defining new predicates: + +<P><PRE>to fullp :datum +output not emptyp :datum +end + +to realwordp :datum +output and wordp :datum not numberp :datum +end + +to digitp :datum +output and numberp :datum equalp count :datum 1 +end +</PRE> + +<P><H2><CODE>Ifelse</CODE> as an Operation</H2> + +<P>So far, we have applied the idea of conditional evaluation only to +complete instructions. It is also possible to choose between two +expressions to evaluate, by using <CODE>ifelse</CODE> as an +operation. + +<P>When used as an operation, <CODE>ifelse</CODE> requires three inputs. The first input +must be either the word <CODE>true</CODE> or the word <CODE>false</CODE>. The second and third +inputs must be lists containing Logo expressions. The output from +<CODE>ifelse</CODE> is the result of evaluating the second input, if the first input +is <CODE>true</CODE>, or the result of evaluating the third input, if the first +input is <CODE>false</CODE>. + +<P><PRE>? <U>print sentence "It's ifelse 2=3 ["correct] ["incorrect]</U> +It's incorrect +? <U>print ifelse emptyp [] [sum 2 3] [product 6 7]</U> +5 +</PRE> + +<P>Here is one of the classic examples of a procedure in which <CODE>ifelse</CODE> is +used as an operation. This procedure is an operation that takes +a number as its input; it outputs the <EM>absolute value</EM> of the +number: + +<P><PRE>to abs :number +output ifelse :number<0 [-:number] [:number] +end +</PRE> + +<P><H2>Expression Lists and Plumbing Diagrams</H2> + +<P><CODE>If</CODE> and <CODE>ifelse</CODE> require <EM>instruction lists</EM> or <EM> +expression lists</EM> as inputs. This requirement is part of their semantics, +not part of the syntax of an instruction. Just as the arithmetic operators +require numbers as inputs (semantics), but those numeric values can be +provided either as explicit numbers in the instruction or as the result of +an arbitrarily complicated subexpression (syntax), the procedures that +require instruction or expression lists as input don't interpret those +inputs until after Logo has set up the plumbing for the instructions that +invoke them. + +<P>What does that mean? Consider the instruction + +<P><PRE>ifelse "false ["stupid "list] [print 23] +</PRE> + +<P>Even though the second input to <CODE>ifelse</CODE>--that is, the first +of the two literal lists--makes no sense as an instruction list, this +instruction will work correctly without printing an error message. The Logo +interpreter knows that <CODE>ifelse</CODE> accepts three inputs, and it sees that +the three input expressions provided are a literal (quoted) word and two +literal lists. It sets up the plumbing without paying any attention to the +semantics of <CODE>ifelse</CODE>; in particular, Logo doesn't care whether the +given inputs are meaningful for use with <CODE>ifelse</CODE>. Then, once <CODE> +ifelse</CODE> starts running, it examines its first input value. Since that input +is the word <CODE>false</CODE>, the <CODE>ifelse</CODE> procedure ignores its second input +completely and executes the instruction in its third input. + +<P>The use of quotation marks and square brackets to indicate literal inputs is +part of the plumbing syntax, not part of the procedure semantics. Don't say, +"<CODE>Ifelse</CODE> requires one predicate input and two inputs in square +brackets." The instruction + +<P><PRE>ifelse last [true false] list ""stupid ""list list bf "sprint 23 +</PRE> + +<P>has a very different plumbing diagram (syntax) from that of the +earlier example, but provides exactly the same input values to <CODE>ifelse</CODE>. + +<P>Consider these two instructions: + +<P><CENTER><IMG SRC="https://people.eecs.berkeley.edu/~bh/v1ch4/printfirst.gif" ALT="figure: printfirst"></CENTER> + +<P>Since the effect of <CODE>print</CODE> is easy to observe, it's not hard +to see the relationship among the instructions, the plumbing diagrams, and +the effects when these instructions are run. Why are brackets used around +the <CODE>first</CODE> expression in one case but not in the other? Because in one +case the expression is how we tell Logo to set up the plumbing diagram, +while in the second case we are giving <CODE>print</CODE> as input a literal list +that just happens to look like an expression. When the context is something +like <CODE>ifelse</CODE> instead of <CODE>print</CODE>, the syntactic situation is really +quite similar, but may be harder to see. Consider this instruction: + +<P><PRE>print ifelse emptyp :a [emptyp :b] [emptyp :c] +</PRE> + +<P>Why do we put brackets around two <CODE>emptyp</CODE> expressions but not +around another similar-looking one? » Draw a plumbing diagram +for this instruction, paying no attention to your mental model of the +meaning of the <CODE>ifelse</CODE> procedure, treating it as if it were the +nonsense procedure <CODE>zot3</CODE>. You will see that the first input to <CODE> +ifelse</CODE> is an expression whose value will be the word <CODE>true</CODE> or the word +<CODE>false</CODE>, because Logo will carry out that first <CODE>emptyp</CODE> computation +before invoking <CODE>ifelse</CODE>. The remaining two inputs, however, are +literal lists that happen to contain the word <CODE>emptyp</CODE> but do not +involve an invocation of <CODE>emptyp</CODE> in the plumbing diagram. Once <CODE> +ifelse</CODE> is actually invoked, precisely one of those two list inputs will be +interpreted as a Logo expression, for which a <EM>new</EM> plumbing diagram +is (in effect) drawn by Logo. The other input list is ignored. + +<P><H2>Stopping a Procedure</H2> + +<P>I'd like to examine more closely one of the examples from the first +chapter: + +<P><PRE>to music.quiz +print [Who is the greatest musician of all time?] +if equalp readlist [John Lennon] [print [That's right!] stop] +print [No, silly, it's John Lennon.] +end +</PRE> + +<P>You now know about almost all of the primitive procedures +used in this example. The only one we haven't discussed is the <CODE>stop</CODE> +command in the second instruction line. + +<P><CODE>Stop</CODE> is a command that takes no inputs. It is only allowed inside +a procedure; you can't type <CODE>stop</CODE> to a top-level prompt. The effect +of <CODE>stop</CODE> is to finish the evaluation of the procedure in which it is +used. Later instructions in the same procedure are skipped. + +<P>Notice that <CODE>stop</CODE> does not stop <EM>all</EM> active procedures. If procedure +A invokes procedure B, and there is a <CODE>stop</CODE> command in procedure B, +then procedure A continues after the point where it invoked B. + +<P>Recall that the <CODE>output</CODE> command also stops the procedure that invokes +it. The difference is that if you're writing an operation, which +should have an output, you use <CODE>output</CODE>; if you're writing a command, +which doesn't have an output, you use <CODE>stop</CODE>. + +<P>In <CODE>music.quiz</CODE>, the effect of the <CODE>stop</CODE> is that if you get the right +answer, the final <CODE>print</CODE> instruction isn't evaluated. The same effect +could have been written this way: + +<P><PRE>ifelse equalp readlist [John Lennon] ~ + [print [That's right!]] ~ + [print [No, silly, it's John Lennon.]] +</PRE> + +<P>The alternative form uses the three-input <CODE>ifelse</CODE> command. One +advantage of using <CODE>stop</CODE> is precisely that it allows the use of +shorter lines. But in this example, where there is only one +instruction after the <CODE>if</CODE>, it doesn't matter much. <CODE>Stop</CODE> is +really useful when you want to stop only in an unusual situation and +otherwise you have a lot of work still to do: + +<P><PRE>to quadratic :a :b :c +local "discriminant +make "discriminant (:b * :b)-(4 * :a * :c) +<U>if :discriminant < 0 [print [No solution.] stop]</U> +make "discriminant sqrt :discriminant +local "x1 +local "x2 +make "x1 (-:b + :discriminant)/(2 * :a) +make "x2 (-:b - :discriminant)/(2 * :a) +print (sentence [x =] :x1 [or] :x2) +end +</PRE> + +<P>This procedure applies the quadratic formula to solve the +equation + +<P><CENTER><EM>ax</EM>²+<EM>bx</EM>+<EM>c</EM>=0</CENTER> + +<P>The only interesting thing about this example for our present purpose +is the fact that sometimes there is no solution. In that case the +procedure <CODE>stop</CODE>s as soon as it finds out. + +<P>Don't forget that you need <CODE>stop</CODE> only if you want to stop a +procedure before its last instruction line. A common mistake made by +beginners who've just learned about <CODE>stop</CODE> is to use it in every +procedure. If you look back at the examples so far you'll see that +many procedures get along fine without invoking <CODE>stop</CODE>. + +<P><H2>Improving the Quiz Program</H2> + +<P>When I first introduced the <CODE>music.quiz</CODE> example in Chapter 1, we +hadn't discussed things like user procedures with inputs. We are now in a +position to generalize the quiz program: + +<P><PRE>to qa :question :answer +print :question +if equalp readlist :answer [print [That's right!] stop] +print sentence [Sorry, it's] :answer +end + +to quiz +qa [Who is the best musician of all time?] [John Lennon] +qa [Who wrote "Compulsory Miseducation"?] [Paul Goodman] +qa [What color was George Washington's white horse?] [white] +qa [how much is 2+2?] [5] +end +</PRE> + +Procedure <CODE>qa</CODE> is our old friend <CODE>music.quiz</CODE>, with variable +inputs instead of a fixed question and answer. <CODE>Quiz</CODE> uses <CODE>qa</CODE> several +times to ask different questions. + +<P>»Here are a couple of suggestions for further improvements you should +be able to make to <CODE>quiz</CODE> and <CODE>qa</CODE>: + +<P>1. <CODE>Qa</CODE> is very fussy about getting one particular answer to a +question. If you answer <CODE>Lennon</CODE> instead of <CODE>John Lennon</CODE>, +it'll tell you you're wrong. There are a couple of ways you might fix +this. One is to look for a single-word answer <EM>anywhere within</EM> +what the user types. So if <CODE>:answer</CODE> is the word <CODE>Lennon</CODE>, +the program will accept "<CODE>Lennon</CODE>," "<CODE>John Lennon</CODE>," or "<CODE>the +Lennon Sisters</CODE>." The second approach would be for <CODE>qa</CODE> to take a +<EM>list</EM> of possible answers as its second input: + +<P><PRE>qa [Who is the best musician of all time?] ~ + [[John Lennon] [Lennon] [the Beatles]] +</PRE> + +<P><CODE>Qa</CODE> then has to use a different predicate, to see if what +the user types is any of the answers in the list. + +<P>2. By giving <CODE>quiz</CODE> a local variable named <CODE>score</CODE>, you could +have <CODE>quiz</CODE> and <CODE>qa</CODE> cooperate to keep track of how many +questions the user gets right. At the end the score could be printed. +(This is an opportunity to think about the stylistic virtues and vices +of letting a subprocedure modify a variable that belongs to its +superprocedure. If you say + +<P><PRE>make "score :score+1 +</PRE> + +<P>inside <CODE>qa</CODE>, doesn't that make <CODE>quiz</CODE> somewhat mysterious +to read? For an alternative, read the next section.) + +<P><H2>Reporting Success to a Superprocedure</H2> + +<P>Suppose we want the quiz program to give the user three tries before +revealing the right answer. There are several ways this could be +programmed. Here is a way that uses the tools you already know about. + +<P>The general idea is that the procedure that asks the question is +written as an <EM>operation,</EM> not as a command. To be exact, it's a +predicate; it outputs <CODE>true</CODE> if the user gets the right answer. +This asking procedure, <CODE>ask.once</CODE>, is invoked as a subprocedure of +<CODE>ask.thrice</CODE>, which is in charge of allowing three tries. <CODE> +ask.thrice</CODE> invokes <CODE>ask.once</CODE> up to three times, but stops if +<CODE>ask.once</CODE> reports success. + +<P><PRE>to ask.thrice :question :answer +repeat 3 [if ask.once :question :answer [stop]] +print sentence [The answer is] :answer +end + +to ask.once :question :answer +print :question +if equalp readlist :answer [print [Right!] output "true] +print [Sorry, that's wrong.] +output "false +end +</PRE> + +<P> +You've seen <CODE>repeat</CODE> in the first chapter, but you haven't been formally +introduced. <CODE>Repeat</CODE> is a command with two inputs. The first input +must be a non-negative whole number. The second input must be a list +of Logo instructions. The effect of <CODE>repeat</CODE> is to evaluate its second +input, the instruction list, the number of times given as the first +input. + +<P>The programming style used in this example is a little controversial. +In general, it's considered a good idea not to mix effect and output +in one procedure. But in this example, <CODE>ask.once</CODE> has an effect (it +prints the question, reads an answer, and comments on its correctness) +and also an output (<CODE>true</CODE> or <CODE>false</CODE>). + +<P>I think the general rule I've just cited is a good rule, but there +are exceptions to it. Using an output of <CODE>true</CODE> or <CODE>false</CODE> to report +the success or failure of some process is one of the situations that +I consider acceptable style. The real point of the rule, I think, +is to separate <EM>calculating</EM> something from <EM> +printing</EM> it. For example, it's a mistake to write procedures like this +one: + +<P><PRE>to <A NAME="prsecond">prsecond :datum +print first butfirst :datum +end +</PRE> + +<P> +A more powerful technique is to write the <CODE>second</CODE> operation +from Chapter 2; instead of + +<P><PRE>prsecond [something or other] +</PRE> + +<P>you can then say + +<P><PRE>print second [something or other] +</PRE> + +<P>It may not be obvious from this example why I call <CODE>second</CODE> +more powerful than <CODE>prsecond</CODE>. But remember that an operation can be +combined with other operations, as in the plumbing diagrams we used +earlier. For example, the operation <CODE>second</CODE> can extract the word <CODE> +or</CODE> from the list as shown here. But you can <EM>also</EM> use it as part of +a more complex instruction to extract the letter <CODE>o</CODE>: + +<P><PRE>print first second [something or other] +</PRE> + +<P>If you'd written the command <CODE>prsecond</CODE> to solve the +first problem, you'd have to start all over again to solve this new +one. (Of course, both of these examples must seem pretty silly; why +bother extracting a word or a letter from this list? But I'm trying +to use examples that are simple enough not to obscure this issue with +the kinds of complications we'll see in more interesting programs.) + +<P>»If you made the improvements to <CODE>quiz</CODE> and <CODE>qa</CODE> that I +suggested earlier, you might like to see if they can fit easily with a new +version of <CODE>quiz</CODE> using <CODE>ask.thrice</CODE>. + +<P><A HREF="../v1-toc2.html">(back to Table of Contents)</A> +<P><A HREF="../v1ch3/v1ch3.html"><STRONG>BACK</STRONG></A> +chapter thread <A HREF="../v1ch5/v1ch5.html"><STRONG>NEXT</STRONG></A> + +<P> +<ADDRESS> +<A HREF="../index.html">Brian Harvey</A>, +<CODE>bh@cs.berkeley.edu</CODE> +</ADDRESS> +</BODY> +</HTML> |