about summary refs log tree commit diff stats
path: root/js/games/nluqo.github.io/~bh/61a-pages/Solutions/week1
diff options
context:
space:
mode:
authorelioat <elioat@tilde.institute>2023-08-23 07:52:19 -0400
committerelioat <elioat@tilde.institute>2023-08-23 07:52:19 -0400
commit562a9a52d599d9a05f871404050968a5fd282640 (patch)
tree7d3305c1252c043bfe246ccc7deff0056aa6b5ab /js/games/nluqo.github.io/~bh/61a-pages/Solutions/week1
parent5d012c6c011a9dedf7d0a098e456206244eb5a0f (diff)
downloadtour-562a9a52d599d9a05f871404050968a5fd282640.tar.gz
*
Diffstat (limited to 'js/games/nluqo.github.io/~bh/61a-pages/Solutions/week1')
-rw-r--r--js/games/nluqo.github.io/~bh/61a-pages/Solutions/week1325
1 files changed, 325 insertions, 0 deletions
diff --git a/js/games/nluqo.github.io/~bh/61a-pages/Solutions/week1 b/js/games/nluqo.github.io/~bh/61a-pages/Solutions/week1
new file mode 100644
index 0000000..0616593
--- /dev/null
+++ b/js/games/nluqo.github.io/~bh/61a-pages/Solutions/week1
@@ -0,0 +1,325 @@
+CS 61A		Week 1		Lab and Homework Solutions
+
+FIRST LAB:
+
+No problems that required original solutions!
+
+SECOND LAB:
+
+1.  Nothing original.
+
+2.  If the last letter is Y, then we have to look at the next-to-last:
+
+(define (plural wd)
+  (if (and (equal? (last wd) 'y)
+	   (not (vowel? (last (bl wd)))))
+      (word (bl wd) 'ies)
+      (word wd 's)))
+
+If you didn't think to use AND in that way, it can be done with nested IFs:
+
+(define (plural wd)
+  (if (equal? (last wd) 'y)
+      (if (vowel? (last (bl wd)))
+	  (word wd 's)
+	  (word (bl wd) 'ies))
+      (word wd 's)))
+
+Or, if that's too messy, with a subprocedure:
+
+(define (plural wd)
+  (if (equal? (last wd) 'y)
+      (y-plural (bl wd))
+      (word wd 's)))
+
+(define (y-plural prefix)
+  (if (vowel? (last prefix))
+      (word prefix 'ys)
+      (word prefix 'ies)))
+
+All of these assume the definition of vowel? in the handout.
+
+
+3.  There are, of course, many possible ways to write this.  None is
+perfectly elegant.  The difficulty is figuring out which of the three
+arguments is smallest, so you can leave it out of the computation.
+The way I like best, I think, is a little tricky:
+
+(define (sum-square-large papa mama baby)
+  (define (square x) (* x x))
+  (cond ((> mama papa) (sum-square-large mama papa baby))
+	((> baby mama) (sum-square-large papa baby mama))
+	(else (+ (square papa) (square mama)))))
+
+I think this way is pretty concise and easy to read.  However, it's okay
+if you did it more straightforwardly like this:
+
+(define (sum-square-large a b c)
+  (define (square x) (* x x))
+  (define (sumsq x y) (+ (square x) (square y)))
+  (cond ((and (<= a b) (<= a c)) (sumsq b c))
+	((and (<= b a) (<= b c)) (sumsq a c))
+	(else (sumsq a b)) ))
+
+If you didn't think of using AND to identify the conditions, it could also
+be done using nested IFs:
+
+(define (sum-square-large a b c)
+  (define (square x) (* x x))
+  (define (sumsq x y) (+ (square x) (square y)))
+  (if (>= a b)
+      (if (>= b c)
+	  (sumsq a b)
+	  (sumsq a c))
+      (if (>= a c)
+	  (sumsq a b)
+	  (sumsq b c))))
+
+Some people wanted to start by solving a subproblem: a function to find
+the two largest numbers.  This can be done, but it's harder:
+
+(define (sum-square-large a b c)
+  (define (square x) (* x x))
+  (define (sumsq nums) (+ (square (first nums)) (square (last nums))))
+  (define (two-largest a b c)
+    (cond ((and (<= a b) (<= a c)) (sentence b c))
+	  ((and (<= b a) (<= b c)) (sentence a c))
+	  (else (sentence a b))))
+  (sumsq (two-largest a b c)))
+
+The trick here is that a function can't return two values, so two-largest
+has to return a sentence containing the two numbers.  This hardly seems
+worth the effort, but the attempt to split the problem into logical pieces
+was well-motivated.  It's a good idea in general, but it didn't work out
+well this time.
+
+See also:
+http://code.google.com/p/jrm-code-project/wiki/ProgrammingArt
+
+
+4.  Since we are examining each word of a sentence, the solution will
+involve a recursion of the form (dupls-removed (bf sent)).  The base
+case is an empty sentence; otherwise, there are two possibilities,
+namely, (first sent) either is or isn't duplicated later in the sentence.
+
+(define (dupls-removed sent)
+  (cond ((empty? sent) '())
+	((member? (first sent) (bf sent))
+	 (dupls-removed (bf sent)))
+	(else (sentence (first sent) (dupls-removed (bf sent))))))
+
+============================================================
+
+HOMEWORK:
+
+1.  The Scheme interpreter applies an ordinary procedure by first evaluating
+all the argument expressions and then invoking the procedure.  Consider first
+one of the examples that worked:
+
+> (new-if (= 2 3) 0 5)
+
+Scheme evaluates this expression as follows:
+
+(a) Evaluate the symbol new-if.  Its value turns out to be an ordinary
+procedure.  Therefore the rest of the combination is evaluated normally.
+
+(b) Evaluate the three argument expressions.  Their values are #f [i.e.,
+false], 0, and 5 respectively.
+
+(c) Apply the procedure new-if to the argument values #f, 0, and 5.  By the
+substitution model, this means we must substitute "#f" for "predicate",
+"0" for "then-clause", and "5" for "else-clause":
+	(cond (#f 0) (else 5))
+
+(d) Evaluate this new expression, getting the value 5.
+
+By contrast, if we'd entered the expression
+
+> (if (= 2 3) 0 5)
+
+Scheme would evaluate it as follows:
+
+(a) Notice that the symbol IF is a keyword, the name of a special form.
+Therefore the rest of the combination is evaluated specially.
+
+(b) Invoke the special form with the UNEVALUATED argument expressions
+"(= 2 3)", "0", and "5".
+
+(c) The "if" evaluation rule then causes its first argument to be
+evaluated.  Since the value is #f, i.e. false, it then evaluates
+the expression "5", whose value is the number 5.  The expression "0"
+is never evaluated.
+
+In the example above, it doesn't make any PRACTICAL difference that the
+expression "5" was evaluated to produce the number 5.  [By the way,
+Scheme uses quotation marks for a special purpose, which isn't what I
+mean here.  I'm just using them to delimit something you're to imagine as
+having typed into the computer.]
+
+Now, on to the square root program.  In the body of sqrt-iter, the third and
+final argument to new-if is the expression
+	(sqrt-iter (improve guess x) x)
+Suppose we invoke sqrt-iter with an expression like
+	(sqrt-iter 1 4)
+Since sqrt-iter and new-if are both ordinary procedures, they are applied
+just like the new-if example I described earlier:
+
+(a) Evaluate the symbol sqrt-iter.  Its value turns out to be an ordinary
+procedure.  Therefore the rest of the combination is evaluated normally.
+
+(b) Evaluate the two argument expressions.  Their values are 1 and 4,
+respectively.
+
+(c) Apply the procedure sqrt-iter to the argument values 1 and 4.  By the
+substitution model, this means we must substitute "1" for "guess" and
+"4" for "x":
+	(new-if (good-enough? 1 4)
+		1
+		(sqrt-iter (improve 1 4)
+			   4))
+
+(d) Evaluate this new expression.  Here is where the problem comes in.
+Since new-if is an ordinary procedure, we follow steps (a)-(d) for this
+sub-evaluation also:
+
+(a) Evaluate the symbol new-if.  Its value turns out to be an ordinary
+procedure.  Therefore the rest of the combination is evaluated normally.
+
+(b) Evaluate the three argument expressions.  The first one turns out
+(after a sequence of (a)-(d) steps) to have the value #f, i.e., false.
+The second has the value 1.  The third invokes sqrt-iter, so we start
+another (a)-(d) sequence of steps just like the first one.  But the first
+one is still pending, waiting for us to finish down here.  That is, the
+evaluation of the original (sqrt-iter 1 4) is waiting for the evaluation
+of the new-if expression, and that's waiting for the evaluation of the new
+sqrt-iter expression.  But THAT will involve evaluating another new-if
+expression, which will...  This is an infinite regress.  You'll never get
+any answer at all.
+
+This business of nested evaluations isn't all wrong.  In the real
+sqrt-iter the same thing happens, with sqrt-iter invoking if, and if
+invoking sqrt-iter, and so on.  The difference is that with the real
+if, a special form, Scheme gets to test whether the good-enough? expression
+is true or false BEFORE it evaluates the inner sqrt-iter expression.  At
+first the good-enough? expression is false, so if invokes sqrt-iter repeatedly
+just as in the new-if version.  But eventually good-enough? returns a true
+[that is, #t] value, and then the inner sqrt-iter expression need not be
+evaluated.  With new-if, we needed to evaluate the inner sqrt-iter before
+we had a chance to see if good-enough? came out true or false.  Therefore
+Scheme never finds out that it's time to stop iterating.
+
+
+2.
+
+(define (squares nums)
+  (if (empty? nums)
+      '()
+      (se (square (first nums))
+	  (squares (bf nums)) )))
+
+
+3.  The tricky part is that the first word of the sentence must be
+treated specially, so there has to be a top-level procedure that handles
+it and also invokes a recursive subprocedure for the rest of the words:
+
+(define (switch sent)
+  (se (switch-first (first sent))
+      (switch-rest (bf sent)) ))
+
+(define (switch-first wd)
+  (cond ((equal? wd 'you) 'I)
+	((equal? wd 'I) 'you)
+	((equal? wd 'me) 'you)
+	(else wd) ))
+
+(define (switch-rest sent)
+  (if (empty? sent)
+      '()
+      (se (switch-one (first sent))
+	  (switch-rest (bf sent)) )))
+
+(define (switch-one wd)
+  (cond ((equal? wd 'you) 'me)
+	((equal? wd 'I) 'you)
+	((equal? wd 'me) 'you)
+	(else wd) ))
+
+4.
+
+(define (ordered? sent)
+  (cond ((empty? (bf sent)) #t)
+	((> (first sent) (first (bf sent))) #f)
+	(else (ordered? (bf sent))) ))
+
+This procedure is written assuming that the argument is a sentence that
+contains at least one word, and that all of the words are numbers.
+
+
+5.
+
+(define (ends-e sent)
+  (cond ((empty? sent) '())
+	((equal? (last (first sent)) 'e)
+	 (se (first sent) (ends-e (bf sent))))
+	(else (ends-e (bf sent)))))
+
+
+6.  Are "and" and "or" ordinary functions or special forms?  The general idea
+of the solution is to type in an expression that will produce an error if all
+its subexpressions are evaluated, and see if they all are.  For example,
+supposing there is no definition for the symbol "x" you could say
+
+> (or 1 2 x)
+
+According to the ordinary evaluation rule, the expressions "1" "2" and "x"
+should all be evaluated before "or" is invoked, so you should get an error
+message complaining about the unbound symbol.  On the other hand, if "or"
+is a special form, you'd expect it to stop as soon as it evaluates the "1"
+and give 1 as its result.
+
+If you try this in Scheme, you don't get an error message.
+This means, most likely, that "or" is a special form whose arguments
+are evaluated one by one.  If there were an error message could you 
+conclude that "or" is ordinary?  No!  "Or" could be a special form
+that evaluates its arguments right-to-left.  For that matter there is
+no reason that "or" couldn't evaluate the middle argument first.  How
+would you test for that?
+
+(Of course, in reality you know that they're special forms because
+the textbook told you so.)
+
+Why might a special form be a good idea?  Here are a few reasons:
+
+(a) Efficiency.  Suppose instead of numbers or symbols I used combinations
+as the arguments to "or", and each combination takes several minutes to
+compute.  If the first one happens to be true, it'd be a shame to waste all
+that time computing the others.
+
+(b) Conditions that depend on each other.  Consider the expression
+
+> (or (= x 0) (> 5 (/ y x)))
+
+This will work fine if "or" is special and evaluates left-to-right,
+otherwise we may be dividing by zero.
+
+Reasons why an ordinary function might be preferred:
+
+(c) Fewer kludges.  It's very easy to read and understand a Lisp program
+if you can be sure that everything that looks like (blah glorp zilch)
+is evaluated by evaluating the subexpressions and then applying the
+procedure "blah" to the arguments "glorp" and "zilch".  Everything that
+looks like a procedure application but is really a special case just makes
+things that much harder to understand.
+
+(d) Creeping featurism.  Where do we stop?  Maybe we should make
+multiplication a special form; after all, in the expression
+
+> (* 0 x)
+
+there's no real reason to evaluate x because we know zero times anything
+is zero.  Pretty soon there are no real functions left in the language.
+
+(e) Functional objects.  You're not expected to know this yet, but
+next week you'll learn that procedures can be manipulated as data,
+just as numbers can.  But special forms aren't procedures and there are
+some things we can't do to them.