The expressive power of the class of procedures functions that we can define at this point is very limited, because we have no way to make tests and to perform different operations depending on the result of a test.
Original | JavaScript | |
For instance, we cannot define a procedure that computes the
absolute
value of a number by testing whether the number is positive, negative,
or zero and taking different actions in the different cases according
to the rule
\[\begin{array}{lll}
|x| & = & \left\{ \begin{array}{rl}
x & \mbox{if $x>0$} \\
0 & \mbox{if $x=0$} \\
-x & \mbox{if $x<0$}
\end{array}
\right.
\end{array}\]
This construct is called a
case analysis, and
there is a special form in Lisp for notating such a case
analysis. It is called
cond (which stands for
conditional), and it is used as follows: (define (abs x) (cond ((> x 0) x) ((= x 0) 0) ((< x 0) (- x)))) (abs (- 5)) The general form of a conditional expression is (cond ($\langle p_1 \rangle $ $\langle e_1 \rangle$) ($\langle p_2 \rangle $ $\langle e_2 \rangle$) $\vdots$ ($\langle p_n \rangle $ $\langle e_n \rangle$)) consisting of the symbol cond followed by parenthesized pairs of expressions ($\langle p\ e \rangle$) called clauses. The first expression in each pair is a predicate—that is, an expression whose value is interpreted as either true or false.[1] |
We start with conditional expressions here, because they fit nicer
into the substitution model. Note that all functions in sections
1.1.1 to 1.1.7 have a single return statement as their body, which
enforces the emphasis on conditional expressions. Conditional
statements are introduced in 1.3.2.
For instance, we cannot declare a function that computes the
absolute value of a number by testing whether the number is nonnegative
and taking different actions in each case according to the rule
\[\begin{array}{lll}
|x| & = & \left\{ \begin{array}{rl}
x & \mbox{if $x \geq 0$} \\
-x & \mbox{otherwise}
\end{array}
\right.
\end{array}\]
We simplify the example so that we can get away with a single
conditional expression.
This construct is a
case analysis and can be written
in JavaScript using a conditional expression as
function abs(x) {
return x >= 0 ? x : - x;
}
which could be expressed in English as If $x$ is greater than or equal to zero, return $x$; otherwise return $- x$.The general form of a conditional expression is $predicate$ ? $consequent$-$expression$ : $alternative$-$expression$ Conditional expressions begin with a $predicate$—that is, an expression whose value is either true or false, two distinguished boolean values in JavaScript. The primitive boolean expressions true and false trivially evaluate to the boolean values true and false, respectively. The $predicate$ is followed by a question mark, the $consequent$-$expression$, a colon, and finally the $alternative$-$expression$. |
Original | JavaScript | |
Conditional expressions are evaluated as follows. The predicate $\langle p_1 \rangle$ is evaluated first. If its value is false, then $\langle p_2 \rangle$ is evaluated. If $\langle p_2 \rangle$'s value is also false, then $\langle p_3 \rangle$ is evaluated. This process continues until a predicate is found whose value is true, in which case the interpreter returns the value of the corresponding consequent expression $\langle e \rangle$ of the clause as the value of the conditional expression. If none of the $\langle p \rangle$'s is found to be true, the value of the cond is undefined. |
To evaluate a conditional expression, the interpreter starts by evaluating the $predicate$ of the expression. If the $predicate$ evaluates to true, the interpreter evaluates the $consequent$-$expression$ and returns its value as the value of the conditional. If the $predicate$ evaluates to false, it evaluates the $alternative$-$expression$ and returns its value as the value of the conditional.[2] |
Original | JavaScript | |
The word predicate is used for procedures that return true or false, as well as for expressions that evaluate to true or false. The absolute-value procedure abs makes use of the primitive predicates >, <, and =.[3] These take two numbers as arguments and test whether the first number is, respectively, greater than, less than, or equal to the second number, returning true or false accordingly. |
The word predicate is used for operators and functions that return true or false, as well as for expressions that evaluate to true or false. The absolute-value function abs makes use of the primitive predicate >=, an operator that takes two numbers as arguments and tests whether the first number is greater than or equal to the second number, returning true or false accordingly. |
Original | JavaScript | |
Here is yet another way to write the absolute-value procedure: (define (abs x) (if (< x 0) (- x) x)) This uses the special form if, a restricted type of conditional that can be used when there are precisely two cases in the case analysis. The general form of an if expression is (if $\langle \textit{predicate} \rangle$ $\langle \textit{consequent}\rangle$ $\langle \textit{alternative}\rangle$) To evaluate an if expression, the interpreter starts by evaluating the $\langle \textit{predicate}\rangle$ part of the expression. If the $\langle \textit{predicate}\rangle$ evaluates to a true value, the interpreter then evaluates the $\langle \textit{consequent}\rangle$ and returns its value. Otherwise it evaluates the $\langle \textit{alternative}\rangle$ and returns its value.[4] |
If we prefer to handle the zero case separately, we can specify the function that computes the absolute value of a number by writing \[\begin{array}{lll} |x| &=& \left\{ \begin{array}{rl} x & \mbox{if $x > 0$} \\ 0 & \mbox{if $x = 0$} \\ -x & \mbox{otherwise} \end{array} \right. \end{array}\] In JavaScript, we express a case analysis with multiple cases by nesting conditional expressions as alternative expressions inside other conditional expressions: function abs(x) { return x > 0 ? x : x === 0 ? 0 : - x; } Parentheses are not needed around the alternative expression x === 0 ? 0 : - x, because the conditional-expression syntactic form is right-associative. The interpreter ignores spaces and line breaks, here inserted for readability to align the ?'s and :'s under the first predicate of the case analysis. The general form of a case analysis is $p$$_1$ ? $e$$_1$ : $p$$_2$ ? $e$$_2$ $\vdots$ : $p$$_n$ ? $e$$_n$ : $final$-$alternative$-$expression$ We call a predicate $p_i$ together with its consequent expression $e_i$ a clause. A case analysis can be seen as a sequence of clauses, followed by a final alternative expression. According to the evaluation of conditional expressions, a case analysis is evaluated by first evaluating the predicate $p$$_1$. If its value is false, then $p$$_2$ is evaluated. If $p$$_2$'s value is also false, then $p$$_3$ is evaluated. This process continues until a predicate is found whose value is true, in which case the interpreter returns the value of the corresponding consequent expression $e$ of the clause as the value of the case analysis. If none of the $p$'s is found to be true, the value of the case analysis is the value of the final alternative expression. |
Original | JavaScript | |
In addition to primitive predicates such as <, =, and >, there are logical composition operations, which enable us to construct compound predicates. The three most frequently used are these:
|
In addition to primitive predicates such as >=, >, <, <=, ===, and !== that are applied to numbers,[5] there are logical composition operations, which enable us to construct compound predicates. The three most frequently used are these:
|
As another example, we can define declare a predicate to test whether one number is greater than or equal to another as
Original | JavaScript |
(define (>= x y) (or (> x y) (= x y))) | function greater_or_equal(x, y) { return x > y || x === y; } |
Original | JavaScript |
(define (>= x y) (not (< x y))) | function greater_or_equal(x, y) { return ! (x < y); } |
Original | JavaScript | |
The function greater_or_equal, when applied to two numbers, behaves the same as the operator >=. Unary operators have higher precedence than binary operators, which makes the parentheses in this example necessary. |
Original | JavaScript |
10 | 10; |
Original | JavaScript |
(+ 5 3 4) | 5 + 3 + 4; |
Original | JavaScript |
(- 9 1) | 9 - 1; |
Original | JavaScript |
(/ 6 2) | 6 / 2; |
Original | JavaScript |
(+ (* 2 4) (- 4 6)) | 2 * 4 + (4 - 6); |
Original | JavaScript |
(define a 3) | const a = 3; |
Original | JavaScript |
(define b (+ a 1)) | const b = a + 1; |
Original | JavaScript |
(+ a b (* a b)) | a + b + a * b; |
Original | JavaScript |
(= a b) | a === b; |
Original | JavaScript |
(if (and (> b a) (< b (* a b))) b a) | b > a && b < a * b ? b : a; |
Original | JavaScript |
(cond ((= a 4) 6) ((= b 4) (+ 6 7 a)) (else 25)) | a === 4 ? 6 : b === 4 ? 6 + 7 + a : 25; |
Original | JavaScript |
(+ 2 (if (> b a) b a)) | 2 + (b > a ? b : a); |
Original | JavaScript |
(* (cond ((> a b) a) ((< a b) b) (else -1)) (+ a 1)) | (a > b ? a : a < b ? b : -1) * (a + 1); |
Original | JavaScript | |
The parentheses around the conditional expressions in the last two statements are necessary because the conditional-expression syntactic form has lower precedence than the arithmetic operators + and *. |
Original | JavaScript |
(define (f x y z) (let ((smallest (if (> x y) (if (> y z) z y) (if (> x z) z x)))) (- (+ (square x) (square y) (square z)) (square smallest)))) | function f(x, y, z) { return square(x) + square(y) + square(z) - // subtract the square of the smallest square(x > y ? (y > z ? z : y) : (x > z ? z : x)); } |
Original | JavaScript | |
Observe that our model of evaluation allows for combinations whose operators are compound expressions. Use this observation to describe the behavior of the following procedure: (define (a-plus-abs-b a b) ((if (> b 0) + -) a b)) | Observe that our model of evaluation allows for applications whose function expressions are compound expressions. Use this observation to describe the behavior of a_plus_abs_b: function plus(a, b) { return a + b; } function minus(a, b) { return a - b; } function a_plus_abs_b(a, b) { return (b >= 0 ? plus : minus)(a, b); } Note that in conditional expressions in JavaScript, we cannot directly use the operators + and - as in Scheme, and instead we must use the names plus and minus because in infix notation, only operator symbols are allowed in the middle, not compound expressions. |
Original | JavaScript |
(define (p) (p)) (define (test x y) (if (= x 0) 0 y)) | function p() { return p(); } function test(x, y) { return x === 0 ? 0 : y; } |
Original | JavaScript |
(test 0 (p)) | test(0, p()); |
Original | JavaScript | |
In applicative-order evaluation of test(0, p()), we need to evaluate the argument expressions before we can evaluate the return expression of the function test. The evaluation of the argument expression p() will not terminate, however: It will keep evaluating application expressions of the form p(), and thus the evaluation of test(0, p()) will not produce a legitimate value. In normal-order evaluation, on the other hand, the function application test(0, p()) would immediately evaluate the return expression of the function test, x === 0 ? 0 : y after replacing the parameter x with 0 and y with p(). The result of the replacing would be 0 === 0 ? 0 : p(). The evaluation of the predicate 0 === 0 results in true and thus the conditional expression evaluates to 0, without any need to evaluate p(). |
Interpreted as either true or falsemeans this: In Scheme, there are two distinguished values that are denoted by the constants #t and #f. When the interpreter checks a predicate's value, it interprets #f as false. Any other value is treated as true. (Thus, providing #t is logically unnecessary, but it is convenient.) In this book we will use names true and false, which are associated with the values #t and #f respectively.
minusoperator -, which, when used with a single operand, as in (- x), indicates negation.