Define a syntactic extension unless with a test and one or more
actions as its subexpressions, so that the actions will be evaluated (in
order) if, but only if, the value of the test is #f. For instance,
the unless-expression
(unless (eof-object? next-character) (write-char next-character target) (kernel (read-char source)))
should have the same effect as
(if (not (eof-object? next-character))
(begin
(write-char next-character target)
(kernel (read-char source))))
An assertion is a Boolean expression that is tested at a
particular point in the execution of a program to make sure that it is true
(usually because it constitutes a precondition for the successful execution
of the next step in the program); if the expression turns out to be false,
execution of the program is halted and an error is reported. For instance,
the display-calendar procedure at the
end of the program that we used in project #4 began with two assertions:
(define display-calendar
(lambda (month year)
;; Test the preconditions.
(if (not (month-number? month))
(error 'first-of-month
"The first argument must be the number of a month"))
(if (not (Gregorian-year? year))
(error 'first-of-month
"The second argument must be a year of the Gregorian calendar"))
...))
The assertions are (month-number? month) and (Gregorian-year?
year). If either of these assertions fails, the program crashes.
Define a syntactic extension assert that has one expression, the
assertion, and does nothing if the value of the assertion is true, but
invokes error if the assertion is false. With the help of assert-expressions, we could write the beginning of display-calendar thus:
(define display-calendar
(lambda (month year)
(assert (month-number? month))
(assert (Gregorian-year? year))
...))
> (display-calendar 38 2005)
(bug) Assertion failed: (month-number? month)
> (display-calendar 7 1362)
(bug) Assertion failed: (Gregorian-year? year)
> (display-calendar 7 1951)
July 1951
Su M Tu W Th F Sa
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Edward Kiser has proposed a replace-expression type for Scheme, as
yet another way to write simple recursions. In its simplest form, a replace-expression would include a variable, an initializing expression
for that variable, an updating expression for that variable, and an
exit-test expression to indicate when the updating process should stop.
Each of the subexpressions would be introduced by an internal keyword --
the initializer with initially, the updater with with, and
the exit test with until. So, for instance, to find the least power
of two greater than 1000, one writes
(replace x initially 1 with (* x 2) until (> x 1000))
The value of this expression is 1024.
Similarly, to build a list containing five occurrences of the symbol foo, one could write
(replace ls initially '() with (cons 'foo ls) until (= (length ls) 5))
Define replace as a syntactic extension.
Use a replace-expression to define a Scheme procedure that computes
the sum of the elements of a given list. (Hint: the value of the variable
in the replace-expression should be a data structure that contains a
running total of the list elements so far inspected and a list of the
elements that have not yet been added to the total.)
Adapt the syntax definition for replace in exercise 3 so that it
also allows a second variant, using the internal keyword while
rather than until, in which the loop keeps running as long as the
value of the test expression is truish. (In other words, the test is a
re-entry test rather than an exit test.)
> (replace x initially 1 with (* x 2) while (<= x 1000)) 1024