Most functions used for branching, such as < and string?, produce either #t or #f. Racket’s branching forms, however, treat any value other than #f as true. We say a true value to mean any value other than #f.
This convention for “true value” meshes well with protocols where #f can serve as failure or to indicate that an optional value is not supplied. (Beware of overusing this trick, and remember that an exception is usually a better mechanism to report failure.)
For example, the member function serves double duty; it can be used to find the tail of a list that starts with a particular item, or it can be used to simply check whether an item is present in a list:
> (member "Groucho" '("Harpo" "Zeppo"))
> (member "Groucho" '("Harpo" "Groucho" "Zeppo"))
> (if (member "Groucho" '("Harpo" "Zeppo")) 'yep 'nope)
> (if (member "Groucho" '("Harpo" "Groucho" "Zeppo")) 'yep 'nope)
4.7.1 Simple Branching: if
In an if form,
(if test-expr then-expr else-expr)
the test-expr is always evaluated. If it produces any value other than #f, then then-expr is evaluated. Otherwise, else-expr is evaluated.
(and expr ...)
(or expr ...)
If evaluation reaches the last expr of an and or or form, then the expr’s value directly determines the and or or result. Therefore, the last expr is in tail position, which means that the above got-milk? function runs in constant space.
Tail Recursion introduces tail calls and tail positions.
4.7.3 Chaining Tests: cond
(cond [test-expr body ...+] ...)
Each test-expr is evaluated in order. If it produces #f, the corresponding bodys are ignored, and evaluation proceeds to the next test-expr. As soon as a test-expr produces a true value, its bodys are evaluated to produce the result for the cond form, and no further test-exprs are evaluated.
The last test-expr in a cond can be replaced by else. In terms of evaluation, else serves as a synonym for #t, but it clarifies that the last clause is meant to catch all remaining cases. If else is not used, then it is possible that no test-exprs produce a true value; in that case, the result of the cond expression is #<void>.
(define (got-milk? lst) (cond [(null? lst) #f] [(eq? 'milk (car lst)) #t] [else (got-milk? (cdr lst))]))
> (got-milk? '(apple banana))
> (got-milk? '(apple milk banana))
The full syntax of cond includes two more kinds of clauses:
The => variant captures the true result of its test-expr and passes it to the result of the proc-expr, which must be a function of one argument.
A clause that includes only a test-expr is rarely used. It captures the true result of the test-expr, and simply returns the result for the whole cond expression.