# 1.1.5功能应用的替代模型

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

<split>为了评估应用程序组合，解释器遵循与运算符组合类似的过程，我们在 [1.1.3](5) 部分中介绍了该过程。 也就是说，解释器评估组合的元素，然后将函数（函数表达式的值）应用到参数（应用程序组合的参数表达式的值）。</split>  <split>更详细地，在评估应用程序组合时，解释器将按以下步骤进行：

*   要评估格式为`![%5Ctextit%7Bfunction-expression%7D](img/489e7221816b9c9d2d4d824af5debe85.jpg) ( ![%5Ctextit%7Bargument-expressions%7D](img/aa7128ed2ea0be56c2e73ad244c7565b.jpg) )`的应用程序组合，请执行以下操作：
    1.  评估应用程序组合的功能表达式，从而得出要应用的功能。
    2.  评估组合的参数表达式。
    3.  将函数应用于参数：
        *   如果函数是原始函数，则只需将相应的数学函数应用于自变量。
        *   如果函数是复合函数，则我们将用每个参数替换为相应参数来评估函数的返回表达式。

To illustrate this process, let's evaluate the application combination

```js
f(5);
```

Evaluating the name `f` results in the function declared in section <ref name="sec:compound-procedures">[1.1.4](6)</ref>. Evaluating the argument expression `5` yields the argument value 5. Now, we retrieve the return expression of `f`:

```js
sum_of_squares(a + 1, a * 2)
```

in which we replace the parameter `a` by the argument 5:

```js
sum_of_squares(5 + 1, 5 * 2)
```</split>  <split>因此，问题减少到对具有两个参数和函数表达式`sum_of_squares`的应用程序组合的求值。 评估此组合涉及三个子问题。 我们必须评估函数表达式以获取要应用的函数，并且必须评估参数表达式以获取参数。 现在`5 + 1`产生6，`5 * 2`产生10，因此我们必须将`sum_of_squares`函数应用于6和10。这些值替换了`sum_of_squares`的返回表达式中的参数`x`和`y`， 将表达式简化为

```js
square(6) + square(10)
```

If we use the declaration of `square` once, this reduces to

```js
(6 * 6) + square(10)
```

which reduces by multiplication to

```js
36 + square(10)
```

A second application of `square` yields

```js
36 + (10 * 10)
```

which reduces by multiplication to

```js
36 + 100
```

and finally by addition to

```js
136
```</split> 

我们刚刚描述的过程称为功能应用程序的_替换模型_。 就本章中的功能而言，它可以作为确定功能应用程序中含义&lt;quote&gt;的模型。 但是，应强调两点：&lt;/quote&gt;

*   替换的目的是帮助我们考虑函数应用程序，而不是提供对解释器实际工作方式的描述。 典型的解释器不会通过操纵函数文本来替换参数值来评估函数应用程序。 实际上，&lt;quote&gt;替换&lt;/quote&gt;是通过使用参数的本地环境来完成的。 当我们详细研究解释器的实现时，我们将在第3章和第4章中对此进行更全面的讨论。
*   在本书的整个过程中，我们将介绍一系列解释器如何工作的日益复杂的模型，最后在第5章中完成对解释器和编译器的完整实现。替代模型只是这些模型中的第一个-一种获得模型的方法 开始正式考虑评估过程。 通常，在对科学和工程中的现象进行建模时，我们从简化，不完整的模型开始。 当我们更详细地研究事物时，这些简单的模型变得不够用，必须用更完善的模型来代替。 替代模型也不例外。 特别是，当我们在第3章讨论&lt;quote&gt;可变数据对函数的使用时，&lt;/quote&gt;我们将看到替换模型崩溃了，必须由更复杂的函数应用模型替换。 [[1]](7#footnote-1)

## 适用顺序与正常顺序

 <split>根据上面给出的评估描述，解释器首先评估函数和参数表达式，然后将结果函数应用于结果参数。 这不是执行评估的唯一方法。 替代的评估模型在需要操作数之前不会评估操作数。 相反，它将首先用参数表达式替换参数，直到获得仅包含运算符的表达式，然后执行评估。 如果我们使用这种方法，

```js
f(5)
```

would proceed according to the sequence of expansions `sum_of_squares(5 + 1, 5 * 2) square(5 + 1) + square(5 * 2) (5 + 1) * (5 + 1) + square(5 * 2) (5 + 1) * (5 + 1) + (5 * 2) * (5 * 2)` followed by the reductions `6 * (5 + 1) + (5 * 2) * (5 * 2) 6 * 6 + (5 * 2) * (5 * 2) 36 + (5 * 2) * (5 * 2) 36 + 10 * (5 * 2) 36 + 10 * 10 36 + 100 136` This gives the same answer as our previous evaluation model, but the process is different. In particular, the evaluations of `5 + 1` and `5 * 2` are each performed twice here, corresponding to the reduction of the expression

```js
x * x
```

with `x` replaced respectively by `5 + 1` and `5 * 2`.</split>

与&lt;quote&gt;评估参数然后应用解释器实际使用的&lt;/quote&gt;方法相反，此替代方法&lt;quote&gt;完全扩展然后减少&lt;/quote&gt;评估方法被称为_正序评估_。 使用，称为_应用顺序评估_。 可以证明，对于可以使用替换建模的功能应用程序（包括本书前两章中的所有功能）并产生合法值，正常顺序和应用顺序评估会产生相同的值。 （请参见练习 [1.5](8#ex_1.5)非法&lt;/quote&gt;值的实例，正常顺序和应用顺序的评估未给出相同的结果。）

JavaScript使用了应用顺序评估，部分原因是通过避免对表达式进行多次评估（如上面所示的评估）而获得的额外效率，更重要的是，当处理时，正常顺序评估变得更加复杂 我们离开了可以通过替代建模的过程领域。 另一方面，正常订单评估可能是一个非常有价值的工具，我们将在第3章和第4章中研究其某些含义。 [[2]](7#footnote-2)

* * *

[[1]](7#footnote-link-1) Despite the simplicity of the substitution idea, it turns out to be surprisingly complicated to give a rigorous mathematical definition of the substitution process. The problem arises from the possibility of confusion between the names used for the parameters of a function and the (possibly identical) names used in the expressions to which the function may be applied. Indeed, there is a long history of erroneous definitions of _substitution_ in the literature of logic and programming semantics. See <citation>Stoy 1977</citation> for a careful discussion of substitution.

[[2]](7#footnote-link-2) In chapter 3 we will introduce _stream processing_, which is a way of handling apparently <quote>infinite</quote> data structures by incorporating a limited form of normal-order evaluation. In section <ref name="sec:lazy-evaluation">[4.2](81)</ref> we will modify the JavaScript interpreter to produce a normal-order variant of JavaScript.

