# 4.1.1评估者的核心

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

<figure>![](img/52026837b6547e9eeda5822c0b35d248.jpg)**[图4.1](74#fig_4.1)** `evaluate` – `apply`循环揭示了计算机语言的本质。</figure>

评估过程可以描述为`evaluate`和`apply`这两个功能之间的相互作用。

## 功能`evaluate`

函数`evaluate`将语句和环境作为参数。 它对语句进行分类并指导其评估。 函数`evaluate`被构造为要评估的表达式的句法类型的案例分析。 为了使函数具有通用性，我们抽象地表示了对语句类型的确定，对各种类型的语句不做任何特定表示。 每种类型的语句都有一个可对其进行测试的谓词，以及一个用于选择其部分的抽象方法。 这种_抽象语法_使我们很容易看到如何使用相同的评估器但使用不同的语法函数集合来更改语言的语法。

<subsubsubsection>Primitive expressions <split>*   如果给定的语句是一个自评估表达式，例如数字，则`evaluate`返回表达式本身。
*   函数`evaluate`必须在环境中查找名称以查找其值。</split></subsubsubsection> <split><subsubsubsection>Language constructs

*   名称的赋值（或声明）必须递归调用`evaluate`，以计算要与变量关联的新值。 必须修改环境以更改（或创建）变量的绑定。
*   条件表达式需要对其部分进行特殊处理，以便评估谓词为true的结果，否则评估替代项。
*   必须通过将函数定义指定的参数和主体与求值环境打包在一起，将函数定义表达式转换为适用的函数。
*   语句序列要求按它们出现的顺序评估其组件语句。
*   一个块需要评估其语句，同时确保该块内的声明对于该块保持局部。
*   当`evaluate`遇到`return`语句时，将评估`return`表达式并将其标记为返回值。</subsubsubsection></split> <subsubsubsection>Combinations <split>*   对于功能应用程序，`evaluate`必须递归地评估功能表达式和应用程序的参数。 结果函数和参数传递给`apply`，后者处理实际的函数应用程序。</split></subsubsubsection> 

这是`evaluate`的定义：

```js
function evaluate(stmt, env) {
   return is_self_evaluating(stmt)
          ?  stmt
        : is_name(stmt)
          ? lookup_name_value(name_of_name(stmt), env)
        : is_constant_declaration(stmt)
          ? eval_constant_declaration(stmt, env)
        : is_variable_declaration(stmt)
          ? eval_variable_declaration(stmt, env)
        : is_assignment(stmt)
          ? eval_assignment(stmt, env)
        : is_conditional_expression(stmt)
          ? eval_conditional_expression(stmt, env)
        : is_function_definition(stmt)
          ? eval_function_definition(stmt, env)
        : is_sequence(stmt)
          ? eval_sequence(sequence_statements(stmt), env)
        : is_block(stmt)
          ? eval_block(stmt, env)
        : is_return_statement(stmt)
          ? eval_return_statement(stmt, env)
        : is_application(stmt)
          ? apply(evaluate(operator(stmt), env),
                  list_of_values(operands(stmt), env))
        : error(stmt, "Unknown statement type in evaluate: ");
}
```

为了清楚起见，已将`evaluate`用作使用条件表达式的案例分析。 这样做的缺点是我们的函数只能处理几种可区分的语句类型，并且不编辑`evaluate`的定义就不能定义新的语句。 在大多数解释器实现中，对表达式类型的分派以数据定向样式完成。 这使用户可以添加`evaluate`可以区分的新型语句和表达式，而无需修改`evaluate`本身的定义。 （参见练习 [4.2](75#ex_4.2) ）

## 应用

函数`apply`具有两个参数，一个函数和应将函数应用到的参数列表。 函数`apply`将函数分为两类：调用`apply_primitive_function`以应用原语； 它通过顺序评估组成函数主体的语句来应用复​​合函数。 用于评估复合函数主体的环境是通过扩展函数所携带的基本环境来构建的，以包括一个框架，该框架将函数的参数绑定到要应用该函数的自变量以及主体的局部 命名为特殊值`no_value_yet`。 用于计算人体局部名称的功能`local_names`也用于_块_，下面将进行说明。 这是`apply`的定义：

```js
function apply(fun, args) {
   if (is_primitive_function(fun)) {
      return apply_primitive_function(fun, args);
   } else if (is_compound_function(fun)) {
      const body = function_body(fun);
      const locals = local_names(body);
      const names = insert_all(function_parameters(fun),
                               locals);
      const temp_values = map(x => no_value_yet,
                              locals);
      const values = append(args,
                            temp_values);			   
      const result =
         evaluate(body,
                  extend_environment(
                      names,
                      values,
                      function_environment(fun)));
      if (is_return_value(result)) {
         return return_value_content(result);
      } else {
          return undefined;
      }
   } else {
      error(fun, "Unknown function type in apply");
   }
}
```

In order to return a value, JavaScript functions need to evaluate a return statement. If a function terminates without return, the value `undefined` is returned. Thus, if the evaluation of the function body yields a return value, the content of the return value is retrieved, and otherwise the value `undefined` is returned.

## 函数参数

`evaluate`处理函数应用程序时，它使用`list_of_values`生成要应用该函数的参数列表。 函数`list_of_values`将组合的操作数作为参数。 它计算每个操作数并返回相应值的列表： [[1]](74#footnote-1)

```js
function list_of_values(exps, env) {
    if (no_operands(exps)) {
        return null;
    } else {
        return pair(evaluate(first_operand(exps), env),
                    list_of_values(rest_operands(exps), env));
   }
}
```

## 有条件的

函数`eval_conditional_expression`在给定的环境中评估条件表达式的谓词部分。 如果结果为true，则评估结果，否则为：

```js
function eval_conditional_expression(stmt, env) {
    return is_true(evaluate(cond_expr_pred(stmt),
                            env))
           ? evaluate(cond_expr_cons(stmt), 
                      env)
           : evaluate(cond_expr_alt(stmt), 
                      env);
}
```

`eval_conditional_expression`中`is_true`的使用突出了实现语言与实现语言之间的联系问题。 谓词以正在实现的语言进行评估，因此会产生该语言的值。 解释器谓词`is_true`将该值转换为可由实现语言中的条件表达式测试的值：事实的元圆表示可能与底层JavaScript的不同。 [[2]](74#footnote-2)

 <split>## 功能定义

函数定义表达式的求值创建一个函数对象，该对象在求值过程中代表函数。 函数对象包含参数和函数定义的主体以及评估函数定义所依据的环境。 根据环境模型，当函数应用于实际参数时，这是需要扩展的环境。

```js
function eval_function_definition(stmt,env) {
    return make_compound_function(
              map(name_of_name,
                  function_definition_parameters(stmt)),
              function_definition_body(stmt),
              env);
}
```</split> 

## 顺序

 <split>`eval`使用函数`eval_sequence`评估语句序列。 请注意，对序列的第一部分的求值可能会产生返回值，在这种情况下，不会评估语句的其余部分。

```js
function eval_sequence(stmts, env) {
    if (is_empty_sequence(stmts)) {
        return undefined;
    } else if (is_last_statement(stmts)) {
            return evaluate(first_statement(stmts),env);
    } else {
        const first_stmt_value = 
            evaluate(first_statement(stmts),env);
        if (is_return_value(first_stmt_value)) {
            return first_stmt_value;
        } else {
            return eval_sequence(
                rest_statements(stmts),env);
        }
    }
}
```</split>  <split>## 积木

`evaluate`使用函数`eval_block`来评估块语句。 在块中声明的常量和变量必须在该块本地。 块语句的评估相对于环境进行评估，该环境通过将块体的本地名称绑定到特殊值`no_value_yet`来扩展当前环境。

```js
function eval_block(stmt, env) {
    const body = block_body(stmt);
    const locals = local_names(body);	    
    const temp_values = map(x => no_value_yet,
                            locals);
    return evaluate(body,
                extend_environment(locals, temp_values, env));
}
```

函数`local_names`收集主体声明中声明的所有名称。 为了使名称包含在`local_names`的列表中，需要在其他任何块或函数之外声明该名称。

```js
function local_names(stmt) {
    if (is_sequence(stmt)) {
        const stmts = sequence_statements(stmt);
        return is_empty_sequence(stmts)
            ? null
            : insert_all(
                  local_names(first_statement(stmts)),
                  local_names(make_sequence(
		               rest_statements(stmts))));
    } else {
       return is_constant_declaration(stmt)
           ? list(constant_declaration_name(stmt))
           : is_variable_declaration(stmt)
             ? list(variable_declaration_name(stmt))
             : null;
    }
}
```

```js
local_names(parse("const x = 1; let y = 2;"));
```

## 退货声明

`evaluate`使用函数`eval_return_statement`评估返回语句。 从序列评估中可以看出，对返回语句的评估结果必须是可识别的，以便即使在return语句之后有语句的情况下，函数主体的评估也可以立即返回。 为此，对return语句的求值将对return表达式求值的结果包装在返回值对象中。

```js
function eval_return_statement(stmt, env) {
    return make_return_value(
               evaluate(return_statement_expression(stmt),
                        env));
}
```</split> 

## 分配和声明

以下函数处理对变量的分配。 它调用`evaluate`查找要分配的值，并将变量和结果值传送到`assign_name_to_value`以安装在指定的环境中。

```js
function eval_assignment(stmt, env) {
    const value = evaluate(assignment_value(stmt), env);
    assign_name_value(assignment_name(stmt), value, env);
    return value;
}
```

 <split>常量和变量的声明以类似的方式处理。 [第4.1.3节](76) 解释了如何区分函数中的变量和常量，以及如何防止分配给常量。

```js
function eval_variable_declaration(stmt, env) {
    set_name_value(variable_declaration_name(stmt),
        evaluate(variable_declaration_value(stmt), env),
        env);
}   
function eval_constant_declaration(stmt, env) {
    set_name_value(constant_declaration_name(stmt),
        evaluate(constant_declaration_value(stmt), env),
        env);
}
```

请注意，常量和变量声明的返回值是`undefined`值，如ECMAScript标准（ &lt;citation&gt;Ecma 1997&lt;/citation&gt; ）所规定。</split> <exercise>Notice that we cannot tell whether the metacircular evaluator evaluates operands from left to right or from right to left. Its evaluation order is inherited from the underlying JavaScript: If the arguments to `pair` in `list_of_values` are evaluated from left to right, then `list_of_values` will evaluate operands from left to right; and if the arguments to `pair` are evaluated from right to left, then `list_of_values` will evaluate operands from right to left. Write a version of `list_of_values` that evaluates operands from left to right regardless of the order of evaluation in the underlying JavaScript. Also write a version of `list_of_values` that evaluates operands from right to left.<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#no_solution_74_1_div">Add solution</button>There is currently no solution available for this exercise. This textbook adaptation is a community effort. Do consider contributing by providing a solution for this exercise, using a Pull Request in [Github](https://github.com/source-academy/sicp).</exercise>

* * *

[[1]](74#footnote-link-1) We could have simplified the `is_application` clause in `evaluate` by using `map` (and stipulating that `operands` returns a list) rather than writing an explicit `list_of_values` function. We chose not to use `map` here to emphasize the fact that the evaluator can be implemented without any use of higher-order functions (and thus could be written in a language that doesn't have higher-order functions), even though the language that it supports will include higher-order functions.

[[2]](74#footnote-link-2) In this case, the language being implemented and the implementation language are the same. Contemplation of the meaning of `is_true` here yields expansion of consciousness without the abuse of substance.

