Ok, I suppose I ought to handle some special forms next.
Ordinarily, in Drscheme, if I have an expression of the from (proc (subexpr) (subexpr)), the program will execute the subexpressions before the main expression (obviously). However, there are some forms (most notably branching constructs) that don't follow the rules exactly.
The simplest is the if statement. In scheme,
if takes the form
(if test-expr true-result [false-result]). This form does test, and if it's true, it does true-result. Otherwise, true-result is never touched. (Same with false-result) I think this should be self-explanatory, but I don't want someone to get lost in the language's semantics.
There are two similar forms,
when and
unless.
(when test result...)
(unless test result...)
The only reason you would use these instead of if is that you can have multiple expressions after the test, for example:
(when (> pressure 10) (open-valve) (ring-bell) (wait 1.5) (check-pressure))
You could also just define a procedure to do those 4 operations, like so:
(define (high-pressure) (open-valve) (ring-bell) (wait 1.5) (check-pressure))
...
(if (> pressure 10) (high-pressure))
But using when and unless is easier.
Now, lets talk about
boolean operations. Scheme provides the usual (and, or, not), and more may be defined depending on your language settings and included libraries (xor, nand, nor...). It is important to note that as far as scheme is concerned, any value that is not false is true, including non-booleans like "hi", 'false (a symbol), and 4. These operations work like if in that they DO short circuit. Thus, the expression
(and (= 4 5) (ring-bell))
will return false without touching the bell. The expression
(and (ring-bell) (= 4 5))
on the other hand, would ring the bell (assuming that that procedure is defined elsewhere), then return false.
Finally (at least for boolean stuff), I'd like to introduce you to
cond, scheme's version of switch. The syntax for cond is a bit different than for ordinary expressions:
Basically, what this form does is, it does the first test. If it's true (ie: not the boolean false), the first result is the result of the whole cond statement. If it is false, it goes on to the next test-result pair, and so on. If it gets through all its
cond clauses without finding a true test, it returns void. Note that you can use the keyword else instead of a test to make it always do that choice. Example:
(cond
[(> 4 5) 42]
[(or (= 4 4) #f) 'moo]
[else "Hello"])
would return 'moo
Using these forms, we could define a procedure to tell us the number of real roots produced by a quadratic equation:
(define (numRealRoots a b c)
(cond
[(> (* b b) (* 4 a c)) 2]
[(= (* b b) (* 4 a c)) 1]
[(< (* b b) (* 4 a c)) 0])) Before I go, I would like to mention one more syntactic form:
begin. (begin expr...) takes any number of subexpressions and returns the result of the last one. This can be useful, for example, when you're writing an if statement, as each result can only be one expression.
(begin0 expr...) works the same way, but it returns the result of the FIRST expression, making it marginally useful as a shorthand way to store and retrieve a variable (for example, when popping items off a stack).
Speaking of storing and retrieving values, another form you'll need to know is
set! . Although scheme is mainly a functional programming language, it does allow you to mutate variables. The syntax is simple: (set! variable value)
e.g.:
(define x 5)
x
(set! x 4)
x
returns 5 and 4 to the interactions window.
OK, so we can set global variables, now wouldn't it be nice if we could also set local variables? The
let form allows us to do that. The syntax is:
(let ([name value]...) body-expr...)
For example, lets say we're defining a function to move a little dude around the screen. Our function will be given his original position and the angle and distance to move him, but we will need to find the x and y offset before feeding it to out graphics and physics engines:
(define (move x y r d)
(let ([movex (* d (cos r))]
[movey (* d (sin r))])
(physics-move x y movex movey)
(graphics-move x y movex movey)))
As much as I'd love to put some information here about the syntax to do other common tasks in scheme, this post is probably long enough already. Join me next time for information on the fun stuff - structures, lists, input, and output. After that we'll start on scheme's classes, graphics, and GUI toolboxes.