# 4.3非确定性计算

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

Note: this section is a work in progress!

在本节中，我们通过将一种支持自动搜索的功能扩展到评估器中，来扩展JavaScript评估器以支持称为_非确定性计算_的编程范例。 与 [4.2](81) 部分中介绍的惰性评估相比，这是对语言的深刻改变。

像流处理一样，不确定性计算对于&lt;quote&gt;生成和测试&lt;/quote&gt;应用程序很有用。 考虑从两个正整数列表开始并找到一对整数（它们的和为素数）的任务（一对来自第一个列表，一个来自第二个列表）。 我们在 [2.2.3](32)[3.5.3](69) 部分中看到了无限流的处理方式。 我们的方法是生成所有可能对的序列，并对其进行过滤以选择总和为素数的对。 无论是像第2章中那样首先真正地生成对的整个序列，还是像第3章中那样对生成和滤波进行交织，对于计算如何组织的基本图像都无关紧要。

非确定性方法引起了不同的印象。 简单地想象一下，我们（以某种方式）从第一个列表中选择一个数字，然后从第二个列表中选择一个数字，并（使用某种机制）要求它们的和为质数。 这由以下功能表示：

```js
function prime_sum_pair(list1, list2) {    
    const a = an_element_of(list1);
    const b = an_element_of(list2);
    require(is_prime(a + b));
    return list(a, b);
}
```

似乎此函数只是解决了问题，而不是指定解决问题的方法。 但是，这是一个合法的不确定性程序。 [[1]](85#footnote-1)

这里的关键思想是，不确定性语言中的表达式可以具有多个可能的值。 例如，`an_element_of`可能返回给定列表的任何元素。 我们的不确定性程序评估器将通过自动选择一个可能的值并跟踪该选择来工作。 如果不满足后续要求，则评估者将尝试其他选择，并且它将继续尝试新的选择，直到评估成功或我们用尽所有选择为止。 就像懒惰的评估者将程序员从值的延迟和强制方法的细节中解放出来一样，非确定性程序评估者也将程序员从选择方式的细节中释放出来。

对比由不确定性评估和流处理引起的不同时间图像是有益的。 流处理使用惰性评估来将可能答案流的组装时间与实际流元素的生成时间解耦。 评估者支持这样一种错觉：所有可能的答案都以永恒的顺序摆在我们面前。 对于非确定性评估，表达式表示对一组可能世界的探索，每个可能性世界由一组选择确定。 一些可能的世界导致死胡同，而另一些则具有有用的价值。 非确定性程序评估器支持时间分支的错觉，并且我们的程序具有不同的可能执行历史。 当我们走到尽头时，我们可以重新访问先前的选择点，然后沿着另一个分支前进。

以下实现的不确定程序评估器称为`amb`评估器，因为它基于称为`amb`的特殊&lt;quote&gt;函数&lt;/quote&gt;。 我们可以在`amb`评估程序驱动程序循环中输入上述`prime_sum_pair`的定义（以及`is_prime`，`is_prime`，`an_element_of`和`require`的定义），并运行以下功能：

```js
Amb-Eval input:
prime_sum_pair(list(1, 3, 5, 8), list(20, 35, 110));
Starting a new problem
Amb-Eval value:
[3, [20, null]]
```

在评估者从每个列表中重复选择元素之后，直到成功选择为止，才获得返回的值。

[第4.3.1节](86)[节4.3.2](87)[4.3.3节](88) 详细介绍了如何实现`amb` 修改普通的计划评估程序。

* * *

[[1]](85#footnote-link-1) We assume that we have previously defined a function `is_prime` that tests whether numbers are prime. Even with `is_prime` defined, the `prime_sum_pair` function may look suspiciously like the unhelpful <quote>pseudo-JavaScript</quote> attempt to define the square-root function, which we described at the beginning of section <ref name="sec:sqrt">[1.1.7](9)</ref>. In fact, a square-root function along those lines can actually be formulated as a nondeterministic program. By incorporating a search mechanism into the evaluator, we are eroding the distinction between purely declarative descriptions and imperative specifications of how to compute answers. We'll go even farther in this direction in section <ref name="sec:logic-programming">[4.4](89)</ref>.

