Constructing Procedures using Lambda Constructing Functions using Lambda Expressions - SICP Comparison Edition" />
Original | JavaScript | |
In using sum as in
section 1.3.1,
it seems terribly awkward to have to define trivial procedures such as
pi-term and
pi-next just so we can use them as
arguments to our higher-order procedure.
Rather than define pi-next and
pi-term, it would be more convenient to
have a way to directly specify the procedure that returns its input incremented by 4and the procedure that returns the reciprocal of its input times its input plus 2.We can do this by introducing the special form lambda, which creates procedures. Using lambda we can describe what we want as (lambda (x) (+ x 4)) and (lambda (x) (/ 1.0 (* x (+ x 2)))) |
In using sum as in
section 1.3.1, it seems
terribly awkward to have to declare trivial functions such as
pi_term and
pi_next just so we can use them as
arguments to our higher-order function. Rather than declare
pi_next and
pi_term, it would be more convenient
to have a way to directly specify the function that returns its input incremented by 4and the function that returns the reciprocal of its input times its input plus 2.We can do this by introducing the lambda expression as a syntactic form for creating functions. Using lambda expressions, we can describe what we want as x => x + 4 and x => 1 / (x * (x + 2)) |
Original | JavaScript |
(define (pi-sum a b) (sum (lambda (x) (/ 1.0 (* x (+ x 2)))) a (lambda (x) (+ x 4)) b)) | function pi_sum(a, b) { return sum(x => 1 / (x * (x + 2)), a, x => x + 4, b); } |
Again using lambda, a lambda expression, we can write the integral procedure function without having to define the auxiliary procedure declare the auxiliary function add-dx: add_dx:
Original | JavaScript |
(define (integral f a b dx) (* (sum f (+ a (/ dx 2.0)) (lambda (x) (+ x dx)) b) dx)) | function integral(f, a, b, dx) { return sum(f, a + dx / 2, x => x + dx, b) * dx; } |
Original | JavaScript | |
In general, lambda is used to create procedures in the same way as define, except that no name is specified for the procedure: (lambda ($formal-parameters$) $body$) The resulting procedure is just as much a procedure as one that is created using define. The only difference is that it has not been associated with any name in the environment. | In general, lambda expressions are used to create functions in the same way as function declarations, except that no name is specified for the function and the return keyword and braces are omitted (if there is only one parameter, the parentheses around the parameter list can also be omitted, as in the examples we have seen).[1] ($parameters$) => $expression$ The resulting function is just as much a function as one that is created using a function declaration statement. The only difference is that it has not been associated with any name in the environment. |
Original | JavaScript |
(define (plus4 x) (+ x 4)) | function plus4(x) { return x + 4; } |
Original | JavaScript |
(define plus4 (lambda (x) (+ x 4))) | const plus4 = x => x + 4; |
Original | JavaScript | |
We can read a lambda expression as follows: ( lambda (x) (+ x 4)) // read as: ^ ^ ^ ^ ^ // the procedure of an argument x that adds x and 4 | We can read a lambda expression as follows: // x => x + 4 // ^ ^ ^ ^ ^ // the function of an argument x that results in the value plus 4 |
Like any expression that has a procedure function as its value, a lambda lambda expression can be used as the function expression in an application combination such as
Original | JavaScript |
((lambda (x y z) (+ x y (square z))) 1 2 3) 12 | ((x, y, z) => x + y + square(z))(1, 2, 3); 12 |
Another use of lambda is in creating local variables. lambda expressions is in creating local names. We often need local variables in our procedures other than those that have been bound as formal parameters. We often need local names in our functions other than those that have been bound as parameters. For example, suppose we wish to compute the function \[\begin{array}{lll} f(x, y)&=&x(1 + x y)^2 +y (1 - y) + (1 + x y)(1 - y) \end{array}\] which we could also express as \[\begin{array}{rll} a &=& 1+xy\\ b &=& 1-y\\ f(x, y) &= &x a^2 +y b + a b \end{array}\] In writing a procedure function to compute $f$, we would like to include as local variables local names not only $x$ and $y$ but also the names of intermediate quantities like $a$ and $b$. One way to accomplish this is to use an auxiliary procedure to bind the local variables: function to bind the local names:
Original | JavaScript |
(define (f x y) (define (f-helper a b) (+ (* x (square a)) (* y b) (* a b))) (f-helper (+ 1 (* x y)) (- 1 y))) | function f(x, y) { function f_helper(a, b) { return x * square(a) + y * b + a * b; } return f_helper(1 + x * y, 1 - y); } |
Of course, we could use a lambda lambda expression to specify an anonymous procedure for binding our local variables. function for binding our local names. The body of f function body then becomes a single call to that procedure: function:
Original | JavaScript |
(define (f x y) ((lambda (a b) (+ (* x (square a)) (* y b) (* a b))) (+ 1 (* x y)) (- 1 y))) | function f_2(x, y) { return ( (a, b) => x * square(a) + y * b + a * b )(1 + x * y, 1 - y); } |
Original | JavaScript | |
This construct is so useful that there is a special form called let to make its use more convenient. Using let, the f procedure could be written as (define (f x y) (let ((a (+ 1 (* x y))) (b (- 1 y))) (+ (* x (square a)) (* y b) (* a b)))) | JavaScript's const cannot be explained as a shorthand for lambda expression/application. Thus we use the weaker formulation "a more convenient way"; this section is therefore a lot shorter in JavaScript than in Scheme. A more convenient way to declare local names is by using constant declarations within the body of the function. Using const, the function f can be written as function f_3(x, y) { const a = 1 + x * y; const b = 1 - y; return x * square(a) + y * b + a * b; } Names that are declared with const inside a block have the body of the immediately surrounding block as their scope.[4]$^,$[5] |
Original | JavaScript | |
The general form of a let expression is (let (($var_1$ $exp_1$) ($var_2$ $exp_2$) $\vdots$ ($var_n$ $exp_n$)) $body$) which can be thought of as saying \begin{array}{ll} \mbox{let}\ &var_1\ \mbox{have the value}\ exp_\ \mbox{and}\\ &var_2\ \mbox{have the value}\ exp_2\ \mbox{and}\\ &\vdots\\ &var_n\ \mbox{have the value}\ exp_n\\ \mbox{in}\ & body \end{array} The first part of the let expression is a list of name-expression pairs. When the let is evaluated, each name is associated with the value of the corresponding expression. The body of the let is evaluated with these names bound as local variables. The way this happens is that the let expression is interpreted as an alternate syntax for ((lambda ($var_1$ $\ldots$ $var_n$) $body$) $exp_1$ $\vdots$ $exp_n$) No new mechanism is required in the interpreter in order to provide local variables. A let expression is simply syntactic sugar for the underlying lambda application. We can see from this equivalence that the scope of a variable specified by a let expression is the body of the let. This implies that:
Sometimes we can use internal definitions to get the same effect as with let. For example, we could have defined the procedure f above as (define (f x y) (define a (+ 1 (* x y))) (define b (- 1 y)) (+ (* x (square a)) (* y b) (* a b))) We prefer, however, to use let in situations like this and to use internal define only for internal procedures.[6] |
Original | JavaScript | |
This is the perfect place to introduce conditional statements, which are of course heavily used in the rest of the book. Their proper substitution model would be a bit technical and quite pointless to explain, so we limit the discussion of the substitution model to conditional expressions. We have seen that it is often useful to declare names that are local to function declarations. When functions become big, we should keep the scope of the names as narrow as possible. Consider for example expmod in exercise 1.26. function expmod(base, exp, m) { return exp === 0 ? 1 : is_even(exp) ? ( expmod(base, exp / 2, m) * expmod(base, exp / 2, m)) % m : (base * expmod(base, exp - 1, m)) % m; } This function is unnecessarily inefficient, because it contains two identical calls: expmod(base, exp / 2, m); While this can be easily fixed in this example using the square function, this is not so easy in general. Without using square, we would be tempted to introduce a local name for the expression as follows: function expmod(base, exp, m) { const half_exp = expmod(base, exp / 2, m); return exp === 0 ? 1 : is_even(exp) ? (half_exp * half_exp) % m : (base * expmod(base, exp - 1, m)) % m; } This would make the function not just inefficient, but actually nonterminating! The problem is that the constant declaration appears outside the conditional expression, which means that it is executed even when the base case exp === 0 is met. To avoid this situation, we provide for conditional statements, and allow return statements to appear in the branches of the statement. Using a conditional statement, we can write the function expmod as follows: function expmod(base, exp, m) { if (exp === 0) { return 1; } else { if (is_even(exp)) { const half_exp = expmod(base, exp / 2, m); return (half_exp * half_exp) % m; } else { return (base * expmod(base, exp - 1, m)) % m; } } }The general form of a conditional statement is if ($predicate$) { $consequent$-$statements$ } else { $alternative$-$statements$ } As for a conditional expression, the interpreter first evaluates the $predicate$. If it evaluates to true, the interpreter evaluates the $consequent$-$statements$ in sequence, and if it evaluates to false, the interpreter evaluates the $alternative$-$statements$ in sequence. Evaluation of a return statement returns from the surrounding function, ignoring any statements in the sequence after the return statement and any statements after the conditional statement. Note that any constant declarations occurring in either part are local to that part, because each part is enclosed in braces and thus forms its own block. |
Original | JavaScript |
(define (f g) (g 2)) | function f(g) { return g(2); } |
Original | JavaScript |
(f square) 4 | f(square); 4 |
Original | JavaScript |
(f (lambda (z) (* z (+ z 1)))) 6 | f(z => z * (z + 1)); 6 |