# 1.3.2函数定义表达式

> 译者： [https://sicp.comp.nus.edu.sg/chapters/20](https://sicp.comp.nus.edu.sg/chapters/20)

<split>在 [1.3.1](19)将输入增加4的函数&lt;/quote&gt;和&lt;quote&gt;将其输入的倒数返回的函数 输入乘以它的输入加2。&lt;/quote&gt;我们可以通过引入创建函数的_函数定义表达式_来做到这一点。 使用函数定义，我们可以描述我们想要的

```js
x => x + 4;
```

and

```js
x => 1.0 / (x * (x + 2));
```</split> 

然后我们的`pi_sum`函数可以表示为，而无需将任何辅助函数声明为

```js
function pi_sum(a,b) {
    return sum(x => 1.0 / (x * (x + 2)),
               a,
               x => x + 4,
               b);
}
```

再次使用函数定义，我们可以编写`integral`函数，而不必声明辅助函数`add_dx`：

```js
function integral(f, a, b, dx) {
    return sum(f,
               a + dx / 2.0,
               x => x + dx,
               b)
           *
           dx;
}
```

 <split>通常，函数定义用于创建函数，类似于函数声明，只是没有为函数 [[1]](20#footnote-1) 指定名称，并且可以省略return关键字和花括号。 [[2]](20#footnote-2) ![%20%5Ctexttt%7B%28%7D%20%5C%20%5Ctextit%7Bparameters%7D%5C%20%5Ctexttt%7B%29%20%3D%3E%20%7D%20%5Ctextit%7Bexpression%7D%20](img/c69fa2efbc304d94b28c1e1d14a8f533.jpg)</split> 

结果函数与使用函数声明语句创建的函数一样多。 唯一的区别是它尚未与环境中的任何名称关联。 事实上，

```js
function plus4(x) { return x + 4; }
```

is equivalent to

```js
const plus4 = x => x + 4;
```

&lt;split&gt;我们可以读取以下函数定义表达式：&lt;/split&gt;

```js
// read                   x         =>           x     +   4
// as:                    ^         ^            ^     ^   ^
// function with argument x that results in the value plus 4
```

像任何以函数为值的表达式一样，函数定义表达式也可以在应用程序组合中用作函数表达式，例如

```js
((x, y, z) => x + y + square(z))(1, 2, 3);
```

or, more generally, in any context where we would normally use a function name.[[3]](20#footnote-3) Note that the JavaScript parser requires parentheses around the function in this case.

## 使用`const`创建本地名称

函数定义的另一种用法是创建本地名称。 除了那些已绑定为参数的函数外，我们通常在函数中还需要局部名称。 例如，假设我们希望计算函数![%20f%28x%2Cy%29%3Dx%281%2Bxy%29%5E2%20%2By%281-y%29%2B%281%2Bxy%29%281-y%29](img/f29c14bb45789ec55e2a89280861d6ee.jpg)，也可以将其表示为\ begin {eqnarray *} a & = & 1 + xy \\ b & = & 1-y \ \ f（x，y）& = & xa ^ 2 + yb + ab \ end {eqnarray *}在编写计算![f](img/dad139946fdd1363ecd86d20700a53a6.jpg)的函数时，我们不仅希望将![x](img/40779fc60a53ff2b70f832ec10cade09.jpg)用作本地名称， 和![y](img/c592009395c2de830215c39f7bb6f97b.jpg)以及![a](img/070b1af5eca3a5c5d72884b536090f17.jpg)和![b](img/6872867a863714d15d9a0d64c20734ce.jpg)等中间数量的名称。 实现此目的的一种方法是使用辅助函数来绑定本地名称：

```js
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);
}
```

当然，我们可以使用函数定义表达式来指定用于绑定本地名称的匿名函数。 然后，`f`的主体成为对该函数的单个调用：

```js
function f(x,y) {
    return ( (a,b) => x * square(a) + 
                      y * b + 
                      a * b
           )(1 + x * y, 1 - y);
}
```

 <split>声明本地名称的一种更方便的方法是在函数体内使用`const`。 使用`const`可以将功能`f`编写为

```js
function f(x, y) {
    const a = 1 + x * y;
    const b = 1 - y;
    return x * square(a) + 
           y * b + 
           a * b;
}
```</split> <split>Names that are declared with `const` inside of function declarations have the surrounding block as their scope.[[4]](20#footnote-4)</split> <split>## 条件语句

We have seen that it is often useful to declare names that are local to function declarations. When functions become big, it will contribute to their readability if we keep the scope of the names as narrow as possible. Consider for example `expmod` in exercise  <ref name="ex:louis-fast-prime">[1.26](17#ex_1.26)</ref> in section <ref name="sec:primality">[1.2.6](17)</ref>.

```js
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:

```js
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:

```js
function expmod(base, exp, m) {
    const to_half = expmod(base, exp / 2, m);
    return exp === 0
           ? 1
           : is_even(exp)
             ? to_half * to_half
               % m
             : base
               * expmod(base, exp - 1, m) 
               % m;
}
```

This would make the function not just inefficient, but actually non-terminating! 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 reached. To avoid this situation, we shall provide for _conditional statements_, and allow for `return` statements to appear in several branches of the statement. Using a conditional statement, the function `expmod` can be written as follows:

```js
function expmod(base, exp, m) {
    if (exp === 0) {
        return 1;
    } else {
        if (is_even(exp)) {
            const to_half = expmod(base, exp / 2, m);    
            return to_half * to_half % m;
        } else {
            return base * expmod(base, exp - 1, m) % m;
        }
    }	    
}
```

The general form of a conditional statement is `if (![%5Ctextit%7Bpredicate%7D](img/28ecfc1c578296d7ed22b45636571912.jpg)) { ![%5Ctextit%7Bconsequent%7D](img/653e9db6445a4a17f54052d4a85b652c.jpg) } else { ![%5Ctextit%7Balternative%7D](img/4459f3d44764db630603b6faff020a16.jpg) }` and, like conditional expressions, their evaluation first evaluates the ![%5Ctextit%7Bpredicate%7D](img/28ecfc1c578296d7ed22b45636571912.jpg). If it evaluates to true, the interpreter evaluates the ![%5Ctextit%7Bconsequent%7D](img/653e9db6445a4a17f54052d4a85b652c.jpg) statements and otherwise the ![%5Ctextit%7Balternative%7D](img/4459f3d44764db630603b6faff020a16.jpg) statements. Note that any constant declarations occurring in either part are local to that part, because both are enclosed in curly braces and thus form their own block.</split> <exercise>Suppose we declare

```js
function f(g) {
   return g(2);
}
```

Then we have

```js
f(square);
```

```js
f(z => z * (z + 1));
```

What happens if we (perversely) ask the interpreter to evaluate the combination `f(f)`? Explain.<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#solution_20_1_div">Solution</button> <solution>让我们使用替代模型来说明会发生什么：

```js
f(f)
f(2)
2(2)
```

The application combination `2(2)` leads to an error, since 2 is neither a primitive nor a compound function.</solution></exercise>

* * *

[[1]](20#footnote-link-1) If there is only one parameter, then the parentheses around the parameter list can be omitted.

[[2]](20#footnote-link-2) In section <ref name="sec:graphics">[2.2.4](33)</ref>, we will extend the syntax of function definition expressions to allow blocks as bodies, as in function declaration statements.

[[3]](20#footnote-link-3) The idea to introduce functions without naming them plays a central role in the ![%5Clambda](img/5e8df2ba7e47a784c714d176ed8bbb7a.jpg) calculus, a mathematical formalism introduced by the mathematical logician Alonzo Church (1941). Church developed the ![%5Clambda](img/5e8df2ba7e47a784c714d176ed8bbb7a.jpg) calculus to provide a rigorous foundation for studying the notions of function and function application. The ![%5Clambda](img/5e8df2ba7e47a784c714d176ed8bbb7a.jpg) calculus has become a basic tool for mathematical investigations of the semantics of programming languages.

[[4]](20#footnote-link-4) Note that a name declared in a function using `const` cannot be used before the declaration fully is evaluated, not even in the right-hand expression of the declaration itself, and regardless whether the same name is declared outside of the function. Thus the program

```js
function h() {
    const x = 1;
    function i() {
        const x = x + 1;
        return x;
    }
    return i();
}
h();
```

leads to an error, because the `x` in `x + 1` is used before its declaration is fully evaluated. The `const` declaration makes sure that the declared name is not used before the evaluation of the declaration is complete. We will return to this issue in section <ref name="sec:internal-definitions">[4.1.6](79)</ref>, after we learn more about evaluation.

