In a previous homework, we saw x-expressions, which were a particular way of writing down structured data. An x-expression is either a string or a symbol-tagged list of x-expressions. There is also a generalization of this idea called an s-expression. In this lab, we will see some of the traditional uses of s-expressions.
An s-expression is one of
- a symbol
- a string
- a number
- a list-of-s-expressions
A list-of-sexpressions is one of
- empty
- (cons <s-expression> <list-of-se-xpressions>)
We saw in a previous lab that the function list
can be used
as a shortcut for writing down lists. There is another shortcut which is
more useful when we want to express large s-expressions. The special
form quote
interprets an s-expression as data, rather than trying to execute it.
(quote (1 2 happy sad)) => (list 1 2 'happy 'sad)
(quote angry) => 'angry
On the other hand, what if we want to make an s-expression which is
mostly constant, except for some pices somewhere in the middle? Nothing inside
of quote
will be evaluated, so we can't use a placeholder to stick another value inside of a quote
d list. There is another form called quasiquote
, which can be used to insert any expressions into an otherwise constant s-expression.
(define my-var 3)
(quote (1 2 my-var)) => (list 1 2 'my-var)
(quasiquote (1 2 (unquote my-var))) => (list 1 2 3)
quote
can also be written as ' (the single quote mark), quasiquote
as ` (the back-quote), and unquote
as , (the comma). In that syntax, the above examples become:
Like
(define my-var 3)
'(1 2 my-var) => (list 1 2 'my-var)
`(1 2 ,my-var) => (list 1 2 3)
list
, quote
will never be of any use writing functions which recursively construct lists. Be sure that you don't use list
or the quote
syntax when you mean cons
.To do:(define-macro apply-to-sexpr
(lambda (function sexpr)
`(,function (quote ,sexpr))))
(apply-to-sexpr first ((1 2) (3 4 5)))
=> (first '((1 2) (3 4)))
=> (first (list (list 1 2) (list 3 4)))
=> (list 1 2)
assert
, which takes an expression and prints out a message if it evaluates to false:
(assert (and (< 1 2) (> 3 4)))
should print out: "(and (< 1 2) (>
3 4)) FAILED"
, but (assert (not (= 1 2)))
should return true. (Read
about the function display
in the Help Desk.)and-list
, which takes a list of expressions and returns
true if all of them are true, or false if any of them are false. It
should behave like and
, only evaluating an expression if all the ones before
it are true. Don't use the built-in and
.define-with-defaults
which
defines a function which allows default arguments. You should read about
case-lambda
in the Help Desk (under the section mzscheme).
In some programs you write you will want to read information from a file. This section introduces the idea of ports (also called streams
in some languages). A port can be in one of two states. It can have
data left, or it can be at the end of the file. Unlike lists, where
you can use empty?
to find out what type of list you have, you find out what
state a port is in by calling e. If
read
returns an end-of-file
object (which can be tested with the predicate eof-object?
), then the file
is over. Otherwise, read
will return the next data in the file. Once
you call read
on a port, the port remembers that you've read from it, and
the next call to read
will return the next data - this is often called a
side-effect.
So what does read
actually return? It returns the next entire s-expression
in the file. This may be a simple symbol or number, or it may be a
big list with sublists and symbols and numbers inside of it
Functions to know:#|
The "template" for using an input-port.
Not really a template in the sense we've been using.
(define (reading-func iport)
(local [(define next-sexpr (read iport))]
(cond [(eof-object? next-sexp) ...]
[else (... next-sexpr ... (reading-func iport))])))
|#
contains-word?
which takes in an input-port
and a symbol and returns true if the symbol is ever read from the file. Then
try (contains-word? (open-input-file "/usr/dict/words" 'zealous))
.list-functions
, which returns a list of all the functions
defined in a Scheme file (you can use your old homework as test cases). How
do you know when you've defined a function? You'll read in something
like (list 'define (list 'my-function-name 'parameter1 'parameter2) ...)).write scheme-doc
, which takes a Scheme file and calculates
the names of all functions, all structures, and all constants defined in
that file. Of course, you'll want to keep these lists separate.