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/ssch6/true | |
parent | 5d012c6c011a9dedf7d0a098e456206244eb5a0f (diff) | |
download | tour-562a9a52d599d9a05f871404050968a5fd282640.tar.gz |
*
Diffstat (limited to 'js/games/nluqo.github.io/~bh/ssch6/true')
-rw-r--r-- | js/games/nluqo.github.io/~bh/ssch6/true | 892 |
1 files changed, 892 insertions, 0 deletions
diff --git a/js/games/nluqo.github.io/~bh/ssch6/true b/js/games/nluqo.github.io/~bh/ssch6/true new file mode 100644 index 0000000..a5c360c --- /dev/null +++ b/js/games/nluqo.github.io/~bh/ssch6/true @@ -0,0 +1,892 @@ +<P> +<A NAME="dumdee"></A> +<P><CENTER><IMG SRC="../ss-pics/tweedle.jpg" ALT="figure: tweedle"></CENTER><P><CENTER>"Contrariwise," continued Tweedledee, "if it was so, +it might be; and if it were so, it would be; but as it isn't, it ain't. +That's logic." +</CENTER><P> + +<HTML> +<HEAD> +<TITLE>Simply Scheme: Introducing Computer Science ch 6: True and False</TITLE> +</HEAD> +<BODY> +<HR> +<CITE>Simply Scheme:</CITE> +<CITE>Introducing Computer Science</CITE> 2/e Copyright (C) 1999 MIT +<H2>Chapter 6</H2> +<H1>True and False</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/ssch06.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="../ssch5/words.html"><STRONG>BACK</STRONG></A> +chapter thread <A HREF="../ssch7/variables.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>We still need one more thing before we can write more interesting programs: +the ability to make decisions. Scheme has a way to say "if this is true, +then do this thing, otherwise do something else." + +<P>Here's a procedure that greets a person: + +<P><PRE>(define (<A NAME="g63"></A>greet name) + (if (equal? (first name) 'professor) + (se '(i hope i am not bothering you) 'professor (last name)) + (se '(good to see you) (first name)))) + +> (greet '(matt wright)) +(GOOD TO SEE YOU MATT) + +> (greet '(professor harold abelson)) +(I HOPE I AM NOT BOTHERING YOU PROFESSOR ABELSON) +</PRE> + +<P>The program greets a person by checking to see if that person is a +professor. If so, it says, "I hope I am not bothering you" and then the +professor's name. But if it's a regular person, the program just says, +"Good to see you," and then the person's first name. + +<P><CODE>If</CODE> takes three arguments. The first has to be either true or +false. +<A NAME="spif"></A> +<A NAME="g64"></A> +(We'll talk in a moment about exactly what true and false look like to +Scheme.) In the above example, the first word of the person's name might or +might not be equal to the word "Professor." The second and third arguments +are expressions; one or the other of them is evaluated depending on the +first argument. The value of the entire <CODE>if</CODE> expression is the value of +either the second or the third argument. + +<P> +You learned in Chapter 2 that Scheme includes a special data type +called <EM>Booleans</EM> to represent true or false +values. There are just two of them: <A NAME="g65"></A><CODE>#t</CODE> for "true" and +<A NAME="g66"></A><CODE>#f</CODE> for "false."<A NAME="dumbfalse"></A><A NAME="text1" HREF="true#ft1">[1]</A> + +<P>We said that the first argument to <CODE>if</CODE> has to be true or false. Of +course, it would be silly to say + +<P> + +<P><PRE>> (if #t (+ 4 5) (* 2 7)) +9 +</PRE> + +<P> +because what's the point of using <CODE>if</CODE> if we already know +which branch will be followed? Instead, as in the <CODE>greet</CODE> example, we call +some procedure whose return value will be either true or false, depending on +the particular arguments we give it. + +<P><H2>Predicates</H2> + +<P>A function that returns either <CODE>#t</CODE> or <CODE>#f</CODE> is called a <EM>predicate.</EM><A NAME="text2" HREF="true#ft2">[2]</A> You've +already seen the <A NAME="g69"></A><CODE>equal?</CODE> predicate. It takes two arguments, which +can be of any type, and returns <CODE>#t</CODE> if the two arguments are the same +value, or <CODE>#f</CODE> if they're different. It's a convention in Scheme that +the names of predicates end with a question mark, but that's just a +convention. Here are some other useful predicates: + +<P> +<PRE>> (member? 'mick '(dave dee dozy beaky mick and tich)) +#T +> (member? 'mick '(john paul george ringo)) +#F +> (member? 'e 'truly) +#F +</PRE> + +<P> +<PRE>> (member? 'y 'truly) +#T +> (= 3 4) +#F +> (= 67 67) +#T +> (> 98 97) +#T +> (before? 'zorn 'coleman) +#F +> (before? 'pete 'ringo) +#T +> (empty? '(abbey road)) +#F +> (empty? '()) +#T +> (empty? 'hi) +#F +> (empty? (bf (bf 'hi))) +#T +> (empty? "") +#T +</PRE> + +<P> + +<P><CODE>Member?</CODE> takes two arguments; it checks to see if the first +<A NAME="memq"></A> +<A NAME="g70"></A> +one is a member of the second. The <A NAME="g71"></A><CODE>=</CODE>, <A NAME="g72"></A><CODE>></CODE>, <A NAME="g73"></A><CODE><</CODE>, +<A NAME="g74"></A><CODE>>=</CODE>, and <A NAME="g75"></A><CODE><=</CODE> functions take two numbers as arguments and do the +obvious comparisons. (By the way, these are exceptions to the convention about +question marks.) <CODE>Before?</CODE> is like <CODE><</CODE>, but it compares two words +<A NAME="g76"></A> +alphabetically. <CODE>Empty?</CODE> checks to see if its argument +<A NAME="g77"></A> +is either the empty word or the empty sentence. + +<P> + +<P>Why do we have both <CODE>equal?</CODE> and <CODE>=</CODE> in Scheme? The first of these +works on any kind of Scheme data, while the second is defined only for +numbers. You could get away with always using <CODE>equal?</CODE>, but the more +specific form makes your program more self-explanatory; people reading the +program know right away that you're comparing numbers. + +<P> + +<P>There are also several predicates that can be used to test the type of their +argument: + +<P> + +<P><PRE>> (<A NAME="g78"></A>number? 'three) +#F +> (number? 74) +#T +> (<A NAME="g79"></A>boolean? #f) +#T +> (boolean? '(the beatles)) +#F +</PRE> + +<P> +<PRE>> (boolean? 234) +#F +> (boolean? #t) +#T +> (<A NAME="g80"></A>word? 'flying) +#T +> (word? '(dig it)) +#F +> (word? 87) +#T +> (<A NAME="g81"></A>sentence? 'wait) +#F +> (sentence? '(what goes on)) +#T +</PRE> + +<P>Of course, we can also define new predicates: + +<P> + +<P><PRE>(define (vowel? letter) + (member? letter 'aeiou)) + +(define (positive? number) + (> number 0)) +</PRE> + +<P><H2>Using Predicates</H2> + +<P>Here's a procedure that returns the absolute value of a number: + +<P> + +<P><PRE>(define (<A NAME="g82"></A>abs num) + (if (< num 0) + (- num) + num)) +</PRE> + +<P> + +<P>(If you call <A NAME="g83"></A><CODE>-</CODE> with just one argument, it returns the +negative of that argument.) Scheme actually provides <A NAME="g84"></A><CODE>abs</CODE> as a +primitive procedure, but we can redefine it. + +<P>Do you remember how to play buzz? You're all sitting around the campfire +and you go around the circle counting up from one. Each person says a +number. If your number is divisible by seven or if one of its digits is a +seven, then instead of calling out your number, you say "buzz." + +<P><PRE>(define (<A NAME="g85"></A>buzz num) + (if (or (divisible? num 7) (member? 7 num)) + 'buzz + num)) + +(define (<A NAME="g86"></A>divisible? big little) + (= (remainder big little) 0)) +</PRE> + +<P><CODE>Or</CODE> can take any number of arguments, each of which must be +true or false. It returns true if any of its arguments are true, that is, if +the first argument is true <EM>or</EM> the second argument is true <EM>or</EM>&hellip (<CODE>Remainder</CODE>, as you know, takes two integers and tells +you what the remainder is when you divide the first by the second. If the +remainder is zero, the first number is divisible by the second.) + +<P><CODE>Or</CODE> is one of three functions in Scheme that combine true or false +values to produce another true or false value. <CODE>And</CODE> returns true if +<A NAME="g87"></A> all of its arguments are true, that is, the first <EM>and</EM> +second <EM>and</EM>&hellip Finally, there's a function <A NAME="g88"></A><CODE>not</CODE> that +takes exactly one argument, returning true if that argument is false and +vice versa. + +<P>In the last example, the procedure we really wanted to write was <CODE>buzz</CODE>, +but we found it useful to define <CODE>divisible?</CODE> also. It's quite common +that the easiest way to solve some problem is to write a <EM>helper +procedure</EM> to do part of the work. In this case the helper procedure +computes a function that's meaningful in itself, but sometimes you'll want +to write procedures with names like <CODE>buzz-helper</CODE> that are useful only +in the context of one particular problem. + +<P>Let's write a program that takes a word as its argument and returns the +plural of that word. Our first version will just put an "s" on the end: + +<P><PRE>(define (plural wd) + (word wd 's)) + +> (plural 'beatle) +BEATLES + +> (plural 'computer) +COMPUTERS + +> (plural 'fly) +FLYS +</PRE> + +<P>This works for most words, but not those that end in "y." Here's +version two: + +<P><PRE>(define (<A NAME="g89"></A>plural wd) + (if (equal? (last wd) 'y) + (word (bl wd) 'ies) + (word wd 's))) +</PRE> + +<P>This isn't exactly right either; it thinks that the plural of +"boy" is "boies." We'll ask you to add some more rules in Exercise +<A HREF="true.html#plurex">6.12</A>. + +<P><H2><CODE>If</CODE> Is a Special Form</H2> + +<P>There are a few subtleties that we haven't told you about yet. First of +all, <A NAME="g90"></A><CODE>if</CODE> is a <A NAME="g91"></A><A NAME="g92"></A>special form. Remember that we're going to +need the value of only one of its last two arguments. It would be wasteful +for Scheme to evaluate the other one. So if you say + +<P><PRE>(if (= 3 3) + 'sure + (factorial 1000)) +</PRE> + +<P><CODE>if</CODE> won't compute the factorial of 1000 before returning +<CODE>sure</CODE>. + +<P>The rule is that <CODE>if</CODE> always evaluates its first argument. If the value +of that argument is true, then <CODE>if</CODE> evaluates its second argument and +returns its value. If the value of the first argument is false, then <CODE>if</CODE> evaluates its third argument and returns that value. + +<P><H2>So Are <CODE>And</CODE> and <CODE>Or</CODE></H2> + +<P><CODE>And</CODE> and <A NAME="g93"></A><CODE>or</CODE> are also <A NAME="g94"></A><A NAME="g95"></A>special forms. They evaluate +<A NAME="g96"></A> their arguments in order from left to right<A NAME="text3" HREF="true#ft3">[3]</A> and stop as soon as they can. For <CODE>or</CODE>, this means +returning true as soon as any of the arguments is true. <CODE>And</CODE> returns +false as soon as any argument is false. This turns out to be useful in +cases like the following: + +<P><PRE>(define (divisible? big small) + (= (remainder big small) 0)) + +(define (<A NAME="g97"></A>num-divisible-by-4? x) + (and (number? x) (divisible? x 4))) + +> (num-divisible-by-4? 16) +#T + +> (num-divisible-by-4? 6) +#F + +> (num-divisible-by-4? 'aardvark) +#F + +> (divisible? 'aardvark 4) +ERROR: AARDVARK IS NOT A NUMBER +</PRE> + +<P>We want to see if <CODE>x</CODE> is a number, and, if so, if it's +divisible by <CODE>4</CODE>. It would be an error to apply <CODE>divisible?</CODE> to a +nonnumber. If <CODE>and</CODE> were an ordinary procedure, the two tests (<CODE>number?</CODE> and <CODE>divisible?</CODE>) would both be evaluated before we would +have a chance to pay attention to the result of the first one. Instead, if +<CODE>x</CODE> turns out not to be a number, our procedure will return <CODE>#f</CODE> +without trying to divide it by <CODE>4</CODE>. + +<P><H2>Everything That Isn't False Is True</H2> + +<P><CODE>#T</CODE> isn't the only true value. In fact, <EM>every</EM> value is +considered true except for <CODE>#f</CODE>. + +<P><PRE>> (if (+ 3 4) 'yes 'no) +YES +</PRE> + +<P>This allows us to have <EM>semipredicates</EM> that give +slightly more information than just true or false. For example, we can +write an integer quotient procedure. That is to say, our procedure will +divide its first argument by the second, but only if the first is evenly +divisible by the second. If not, our procedure will return <CODE>#f</CODE>. + +<P><PRE>(define (<A NAME="g98"></A>integer-quotient big little) + (if (divisible? big little) + (/ big little) + #f)) + +> (integer-quotient 27 3) +9 + +> (integer-quotient 12 5) +#F +</PRE> + +<P><CODE>And</CODE> and <CODE>or</CODE> are also semipredicates. We've already explained +that <CODE>or</CODE> returns a true result as soon as it evaluates a true +argument. The particular true value that <CODE>or</CODE> returns is the value of +that first true argument: + +<P><PRE>> (or #f 3 #f 4) +3 +</PRE> + +<P><CODE>And</CODE> returns a true value only if all of its arguments are +true. In that case, it returns the value of the last argument: + +<P><PRE>> (and 1 2 3 4 5) +5 +</PRE> + +<P>As an example in which this behavior is useful, we can rewrite +<CODE>integer-quotient</CODE> more tersely: + +<P><PRE>(define (integer-quotient big little) ;; alternate version + (and (divisible? big little) + (/ big little))) +</PRE> + +<P><H2>Decisions, Decisions, Decisions</H2> + +<P><CODE>If</CODE> is great for an either-or choice. But sometimes there are several +possibilities to consider: + +<P><PRE>(define (roman-value letter) + (if (equal? letter 'i) + 1 + (if (equal? letter 'v) + 5 + (if (equal? letter 'x) + 10 + (if (equal? letter 'l) + 50 + (if (equal? letter 'c) + 100 + (if (equal? letter 'd) + 500 + (if (equal? letter 'm) + 1000 + 'huh?)))))))) +</PRE> + +<P>That's pretty hideous. Scheme provides a shorthand notation for +situations like this in which you have to choose from among several +possibilities: the <A NAME="g99"></A><A NAME="g100"></A>special form <A NAME="g101"></A><CODE>cond</CODE>. +<A NAME="cond"></A> + +<P><PRE>(define (<A NAME="g102"></A>roman-value letter) + (cond ((equal? letter 'i) 1) + ((equal? letter 'v) 5) + ((equal? letter 'x) 10) + ((equal? letter 'l) 50) + ((equal? letter 'c) 100) + ((equal? letter 'd) 500) + ((equal? letter 'm) 1000) + (else 'huh?))) +</PRE> + +<P>The tricky thing about <CODE>cond</CODE> is that it doesn't use parentheses in quite +<A NAME="g103"></A> the same way as the rest +of Scheme. Ordinarily, parentheses mean procedure invocation. In <CODE>cond</CODE>, <EM>most</EM> of the parentheses still mean that, but <EM>some</EM> of +them are used to group pairs of tests and results. We've reproduced the +same <CODE>cond</CODE> expression below, indicating the funny ones in boldface. + +<P><PRE>(define (roman-value letter) + (cond <STRONG><BIG>(</BIG></STRONG>(equal? letter 'i) 1<STRONG><BIG>)</BIG></STRONG> + <STRONG><BIG>(</BIG></STRONG>(equal? letter 'v) 5<STRONG><BIG>)</BIG></STRONG> + <STRONG><BIG>(</BIG></STRONG>(equal? letter 'x) 10<STRONG><BIG>)</BIG></STRONG> + <STRONG><BIG>(</BIG></STRONG>(equal? letter 'l) 50<STRONG><BIG>)</BIG></STRONG> + <STRONG><BIG>(</BIG></STRONG>(equal? letter 'c) 100<STRONG><BIG>)</BIG></STRONG> + <STRONG><BIG>(</BIG></STRONG>(equal? letter 'd) 500<STRONG><BIG>)</BIG></STRONG> + <STRONG><BIG>(</BIG></STRONG>(equal? letter 'm) 1000<STRONG><BIG>)</BIG></STRONG> + <STRONG><BIG>(</BIG></STRONG>else 'huh?<STRONG><BIG>)</BIG></STRONG> )) +</PRE> + +<P><CODE>Cond</CODE> takes any number of arguments, each of which is <EM>two +expressions</EM> inside a pair of parentheses. Each argument is called a <EM><A NAME="g104"></A><A NAME="g105"></A>cond clause.</EM> In the example above, one typical clause is + +<P><PRE><STRONG><BIG>(</BIG></STRONG>(equal? letter 'l) 50<STRONG><BIG>)</BIG></STRONG> +</PRE> + +<P>The outermost parentheses on that line enclose two expressions. +The first of the two expressions (the <EM>condition</EM>) is taken as +true or false, just like the first argument to <CODE>if</CODE>. The second +expression of each pair (the <EM>consequent</EM>) is a candidate for +the return value of the entire <CODE>cond</CODE> invocation. + +<P><CODE>Cond</CODE> examines its arguments from left to right. Remember that since +<CODE>cond</CODE> is a special form, its arguments are not evaluated ahead of time. +For each argument, <CODE>cond</CODE> evaluates the first of the two expressions +within the argument. If that value turns out to be true, then <CODE>cond</CODE> +evaluates the second expression in the same argument, and returns that value +without examining any further arguments. But if the value is false, then +<CODE>cond</CODE> does <EM>not</EM> evaluate the second expression; instead, it goes +on to the next argument. + +<P>By convention, the last argument always starts with the word <A NAME="g106"></A><CODE>else</CODE> +instead of an expression. You can think of this as representing a true +value, but <CODE>else</CODE> doesn't mean true in any other context; you're only +allowed to use it as the condition of the last clause of a <CODE>cond</CODE>.<A NAME="text4" HREF="true#ft4">[4]</A> + +<P>Don't get into bad habits of thinking about the appearance of +<CODE>cond</CODE> clauses in terms of "two parentheses in a row." +That's often the case, but not always. For example, here is a procedure that +translates Scheme true or false values (<CODE>#t</CODE> and <CODE>#f</CODE>) +into more human-readable words <CODE>true</CODE> and <CODE>false</CODE>. + +<P><PRE>(define (<A NAME="g107"></A>truefalse value) + (cond (value 'true) + (else 'false))) + +> (truefalse (= 2 (+ 1 1))) +TRUE + +> (truefalse (= 5 (+ 2 2))) +FALSE +</PRE> + +<P>When a <CODE>cond</CODE> tests several possible conditions, they might not +be <A NAME="g108"></A><A NAME="g109"></A>mutually exclusive.<A NAME="text5" +HREF="true#ft5">[5]</A> This can be either a source of error or an advantage in +writing efficient programs. The trick is to make the <EM>most +restrictive</EM> test first. For example, it would be an error to say + +<P><PRE>(cond ((number? (first sent)) &hellip) ;; wrong + ((empty? sent) &hellip) + &hellip) +</PRE> + +<P>because the first test only makes sense once you've already +established that there <EM>is</EM> a first word of the sentence. +On the other hand, you don't have to say + +<P><PRE>(cond ((empty? sent) &hellip) + ((and (not (empty? sent)) (number? (first sent))) &hellip) + &hellip) +</PRE> + +<P>because you've already established that the sentence is nonempty +if you get as far as the second clause. + +<P><H2><CODE>If</CODE> Is Composable</H2> + +<P>Suppose we want to write a <CODE>greet</CODE> procedure that works like this: + +<P><PRE>> (greet '(brian epstein)) +(PLEASED TO MEET YOU BRIAN - HOW ARE YOU?) + +> (greet '(professor donald knuth)) +(PLEASED TO MEET YOU PROFESSOR KNUTH - HOW ARE YOU?) +</PRE> + +<P>The response of the program in these two cases is almost the same; +the only difference is in the form of the person's name. + +<P>This procedure could be written in two ways: + +<P><PRE>(define (greet name) + (if (equal? (first name) 'professor) + (se '(pleased to meet you) + 'professor + (last name) + '(- how are you?)) + (se '(pleased to meet you) + (first name) + '(- how are you?)))) + +(define (greet name) + (se '(pleased to meet you) + (if (equal? (first name) 'professor) + (se 'professor (last name)) + (first name)) + '(- how are you?))) +</PRE> + +<P>The second version avoids repeating the common parts of the +response by using <CODE>if</CODE> within a larger expression. + +<P>Some people find it counterintuitive to use <CODE>if</CODE> as we did in the second +version. Perhaps the reason is that in some other programming languages, +<CODE>if</CODE> is a "command" instead of a function like any other. A mechanism +that selects one part of a program to run, and leaves out another part, may +seem too important to be a mere argument subexpression. But in Scheme, the +value returned by <EM>every</EM> function can be used as part of a larger +expression.<A NAME="text6" HREF="true#ft6">[6]</A> + +<P>We aren't saying anything new here. We've already explained the idea of +composition of functions, and we're just making the same point again about +<CODE>if</CODE>. But we've learned that many students expect <CODE>if</CODE> to be an +exception, so we're taking the opportunity to emphasize the point: There are +no exceptions to this rule. + +<P><H2>Pitfalls</H2> + +<P>The biggest pitfall in this chapter is the unusual notation of <CODE>cond</CODE>. Keeping track of the parentheses that mean function invocation, as +usual, and the parentheses that just group the parts of a <CODE>cond</CODE> clause +is tricky until you get accustomed to it. + +<P>Many people also have trouble with the asymmetry of the <A NAME="g110"></A><CODE>member?</CODE> +predicate. The first argument is something small; the second is something +big. (The order of arguments is the same as the order of a typical English +sentence about membership: "Is Mick a member of the Beatles?") +It seems pretty obvious when you look at an example in which both +arguments are quoted constant values, but you can get in trouble when you +define a procedure and use its parameters as the arguments to <CODE>member?</CODE>. +Compare writing a procedure that says, "does the letter E appear in this +word?" with one that says, "is this letter a vowel?" + +<P>Many people try to use <CODE>and</CODE> and <CODE>or</CODE> with the full flexibility +of the corresponding English words. Alas, Scheme is not English. For +example, suppose you want to know whether the argument to a procedure is +either the word <CODE>yes</CODE> or the word <CODE>no</CODE>. You can't say + +<P> +<PRE>(equal? argument (or 'yes 'no)) ; wrong! +</PRE> + +<P>This sounds promising: "Is the <CODE>argument</CODE> <CODE>equal</CODE> to +the word <CODE>yes</CODE> <CODE>or</CODE> the word <CODE>no</CODE>?" But the arguments to <CODE>or</CODE> must be true-or-false values, not things you want to check for equality +with something else. You have to make two separate equality tests: + +<P><PRE>(or (equal? argument 'yes) (equal? argument 'no)) +</PRE> + +<P>In this particular case, you could also solve the problem by saying + +<P><PRE>(member? argument '(yes no)) +</PRE> + +<P>but the question of trying to use <CODE>or</CODE> as if it were English +comes up in other cases for which <CODE>member?</CODE> won't help. + +<P>This isn't exactly a pitfall, because it won't stop your program from +working, but programs like + +<P><PRE>(define (odd? n) + (if (not (even? n)) #t #f)) +</PRE> + +<P>are redundant. Instead, you could just say + +<P><PRE>(define (odd? n) + (not (even? n))) +</PRE> + +<P>since the value of <CODE>(not (even? n))</CODE> is already <CODE>#t</CODE> or +<CODE>#f</CODE>. + +<P><H2>Boring Exercises</H2> + +<P><B>6.1</B> What values are printed when you type these expressions to Scheme? (Figure +it out in your head before you try it on the computer.) + +<P><PRE>(cond ((= 3 4) '(this boy)) + ((< 2 5) '(nowhere man)) + (else '(two of us))) + +(cond (empty? 3) + (square 7) + (else 9)) + +(define (<A NAME="g111"></A>third-person-singular verb) + (cond ((equal? verb 'be) 'is) + ((equal? (last verb) 'o) (word verb 'es)) + (else (word verb 's)))) + +(third-person-singular 'go) +</PRE> + + +<P> + +<B>6.2</B> What values are printed when you type these expressions to Scheme? (Figure +it out in your head before you try it on the computer.) + +<P><PRE>(or #f #f #f #t) + +(and #f #f #f #t) + +(or (= 2 3) (= 4 3)) + +(not #f) + +(or (not (= 2 3)) (= 4 3)) + +(or (and (= 2 3) (= 3 3)) (and (< 2 3) (< 3 4))) +</PRE> + +<P><B>6.3</B> Rewrite the following procedure using a <CODE>cond</CODE> instead of the <CODE>if</CODE>s: + +<P><PRE>(define (<A NAME="g112"></A>sign number) + (if (< number 0) + 'negative + (if (= number 0) + 'zero + 'positive))) +</PRE> + +<P> +<B>6.4</B> Rewrite the following procedure using an <CODE>if</CODE> instead of the <CODE>cond</CODE>: + +<P><PRE>(define (<A NAME="g113"></A>utensil meal) + (cond ((equal? meal 'chinese) 'chopsticks) + (else 'fork))) +</PRE> + +<P> +<P> +<H2>Real Exercises</H2> +<P> +Note: Writing helper procedures may be useful in solving some of these +problems. + +<P><B>6.5</B> Write a procedure <A NAME="g114"></A><CODE>european-time</CODE> to convert a time from American +AM/PM notation into European 24-hour notation. Also +write <A NAME="g115"></A><CODE>american-time</CODE>, which does the opposite: + +<P><PRE>> (european-time '(8 am)) +8 + +> (european-time '(4 pm)) +16 + +> (american-time 21) +(9 PM) + +> (american-time 12) +(12 PM) + +> (european-time '(12 am)) +24 +</PRE> + +<P>Getting noon and midnight right is tricky. + + +<P> +<B>6.6</B> Write a predicate <A NAME="g116"></A><CODE>teen?</CODE> that returns true if its argument is between +13 and 19. + + +<P> +<B>6.7</B> Write a procedure <A NAME="g117"></A><CODE>type-of</CODE> that takes anything as its argument and +returns one of the words <CODE>word</CODE>, <CODE>sentence</CODE>, <CODE>number</CODE>, or +<CODE>boolean</CODE>: + +<P><PRE>> (type-of '(getting better)) +SENTENCE + +> (type-of 'revolution) +WORD + +> (type-of (= 3 3)) +BOOLEAN +</PRE> + +<P>(Even though numbers are words, your procedure should return <CODE>number</CODE> if its argument is a number.) + +<P>Feel free to check for more specific types, such as "positive integer," +if you are so inclined. + + +<P> +<B>6.8</B> Write a procedure <A NAME="g118"></A><CODE>indef-article</CODE> that works like this: +<PRE>> (indef-article 'beatle) +(A BEATLE) + +> (indef-article 'album) +(AN ALBUM) +</PRE> + +<P>Don't worry about silent initial consonants like the <CODE>h</CODE> in <CODE>hour</CODE>. + + +<P> +<B>6.9</B> Sometimes you must choose the singular or the plural of a word: <CODE>1 book</CODE> but <CODE>2 books</CODE>. Write a procedure <A NAME="g119"></A><CODE>thismany</CODE> that takes +two arguments, a number and a singular noun, and combines them appropriately: + +<P><PRE>> (thismany 1 'partridge) +(1 PARTRIDGE) + +> (thismany 3 'french-hen) +(3 FRENCH-HENS) +</PRE> + + +<P> +<P> +<B>6.10</B> Write a procedure <A NAME="g120"></A><CODE>sort2</CODE> that takes as its argument a sentence +containing two numbers. It should return a sentence containing the same two +numbers, but in ascending order: + +<P><PRE>> (sort2 '(5 7)) +(5 7) + +> (sort2 '(7 5)) +(5 7) +</PRE> + +<P> +<B>6.11</B> Write a predicate <A NAME="g121"></A><CODE>valid-date?</CODE> that takes three numbers as arguments, +representing a month, a day of the month, and a year. Your procedure should +return <CODE>#t</CODE> if the numbers represent a valid date (e.g., it isn't the +31st of September). February has 29 days if the year is divisible by 4, +except that if the year is divisible by 100 it must also be divisible by 400. + +<P><PRE>> (valid-date? 10 4 1949) +#T + +> (valid-date? 20 4 1776) +#F + +> (valid-date? 5 0 1992) +#F + +> (valid-date? 2 29 1900) +#F + +> (valid-date? 2 29 2000) +#T +</PRE> + +<P> +<B>6.12</B> Make <A NAME="g122"></A><CODE>plural</CODE> handle correctly words that end in <CODE>y</CODE> but have a +vowel before the <CODE>y</CODE>, such as <CODE>boy</CODE>. Then teach it about words that +end in <CODE>x</CODE> (box). What other special cases can you find? + +<P><A NAME="plurex"></A> + + +<P> +<B>6.13</B> Write a better <A NAME="g123"></A><CODE>greet</CODE> procedure that understands as many different +kinds of names as you can think of: + +<P><PRE>> (greet '(john lennon)) +(HELLO JOHN) + +> (greet '(dr marie curie)) +(HELLO DR CURIE) + +> (greet '(dr martin luther king jr)) +(HELLO DR KING) + +> (greet '(queen elizabeth)) +(HELLO YOUR MAJESTY) + +> (greet '(david livingstone)) +(DR LIVINGSTONE I PRESUME?) +</PRE> + + +<P> +<B>6.14</B> Write a procedure <A NAME="g124"></A><CODE>describe-time</CODE> that takes a number of seconds as its +argument and returns a more useful description of that amount of time: +<A NAME="desctime"></A> + +<P><PRE>> (describe-time 45) +(45 SECONDS) + +> (describe-time 930) +(15.5 MINUTES) + +> (describe-time 30000000000) +(9.506426344208686 CENTURIES) +</PRE> + +<P> + +<HR> +<A NAME="ft1" HREF="true#text1">[1]</A> In <EM>some</EM> +versions of Scheme, the <A NAME="g67"></A><A NAME="g68"></A>empty sentence is considered false. That +is, <CODE>()</CODE> and <CODE>#f</CODE> may be the same thing. The reason that we can't +be definite about this point is that older versions of Scheme follow the +traditional Lisp usage, in which the empty sentence is false, but since then +a standardization committee has come along and insisted that the two values +should be different. In this book we'll consider them as different, but +we'll try to avoid examples in which it matters. The main point is that you +shouldn't be surprised if you see something like this: + +<P><PRE>> (= 3 4) +() +</PRE> + +<P>in the particular implementation of Scheme that you're using. +<P> +<A NAME="ft2" HREF="true#text2">[2]</A> Why is it called that? Think about an English +sentence, such as "Ringo is a drummer." As you may remember from elementary +school, "Ringo" is the <EM>subject</EM> of that sentence, and "is a +drummer" is the <EM>predicate.</EM> That predicate could be truthfully +attached to some subjects but not others. For example, it's true of "Neil +Peart" but not of "George Harrison." So the predicate "is a drummer" +can be thought of as a function whose value is true or false.<P> +<A NAME="ft3" HREF="true#text3">[3]</A> Since you +can start a new line in the middle of an expression, in some cases the +arguments will be "top to bottom" rather than "left to right," but don't +forget that Scheme doesn't care about line breaks. That's why Lisp +programmers always talk as if their programs were written on one enormously +long line.<P> +<A NAME="ft4" HREF="true#text4">[4]</A> What if you don't use an <CODE>else</CODE> +clause at all? If none of the clauses has a true condition, then the return +value is unspecified. In other words, always use <CODE>else</CODE>.<P> +<A NAME="ft5" HREF="true#text5">[5]</A> Conditions are mutually +exclusive if only one of them can be true at a time.<P> +<A NAME="ft6" HREF="true#text6">[6]</A> Strictly speaking, since the argument expressions to a +special form aren't evaluated, <CODE>if</CODE> is a function whose domain is +expressions, not their values. But many special forms, including <CODE>if</CODE>, +<CODE>and</CODE>, and <CODE>or</CODE>, are designed to act as if they were ordinary +functions, the kind whose arguments Scheme evaluates in advance. The only +difference is that it is sometimes possible for Scheme to figure out the +correct return value after evaluating only some of the arguments. Most of +the time we'll just talk about the domains and ranges of these special forms +as if they were ordinary functions.<P> +<P><A HREF="../ss-toc2.html">(back to Table of Contents)</A><P> +<A HREF="../ssch5/words.html"><STRONG>BACK</STRONG></A> +chapter thread <A HREF="../ssch7/variables.html"><STRONG>NEXT</STRONG></A> + +<P> +<ADDRESS> +<A HREF="../index.html">Brian Harvey</A>, +<CODE>bh@cs.berkeley.edu</CODE> +</ADDRESS> +</BODY> +</HTML> |