灯下 登录
计算机科学 / SICP / subsection 中英对照

4.3.1 Amb and Search

To extend Scheme to support nondeterminism, we introduce a new special form

called amb.

The expression

为了将 Scheme 扩展以支持非确定性 (nondeterminism),我们引入一种称为 `amb` 的新特殊形式。表达式

returns the value of one of the n expressions ⟨

返回 n 个表达式 ⟨

e
i

“ambiguously.” For example, the expression

中某一个的值,以“模棱两可 (ambiguously)”的方式进行选择。例如,表达式

can have six possible values:

共有六个可能的值:

Amb with a single choice produces an ordinary (single) value.

只有单个选择的 `amb` 产生普通的(单一)值。

Amb with no choices—the expression (amb)—is an expression

with no acceptable values. Operationally, we can think of (amb) as an

expression that when evaluated causes the computation to “fail”: The

computation aborts and no value is produced. Using this idea, we can express

the requirement that a particular predicate expression p must be true as

follows:

没有任何选择的 `amb`——即表达式 `(amb)`——是一个没有可接受值的表达式。从操作层面来看,可以将 `(amb)` 视为这样一种表达式:对它求值会使计算“失败 (fail)”——计算中止,不产生任何值。利用这一思想,我们可以如下表达某个谓词表达式 `p` 必须为真的要求:

With amb and require, we can implement the an-element-of

procedure used above:

借助 `amb` 和 `require`,我们可以实现上面用到的 `an-element-of` 过程:

An-element-of fails if the list is empty. Otherwise it ambiguously

returns either the first element of the list or an element chosen from the rest

of the list.

`An-element-of` 在表为空时失败。否则,它以非确定性的方式返回表的第一个元素,或从表的其余元素中选取一个元素。

We can also express infinite ranges of choices. The following procedure

potentially returns any integer greater than or equal to some given n:

我们也可以表达无限范围的选择。下面这个过程可能返回任何大于或等于给定 `n` 的整数:

This is like the stream procedure integers-starting-from described in

3.5.2, but with an important difference: The stream procedure

returns an object that represents the sequence of all integers beginning with

n, whereas the amb procedure returns a single integer.

这类似于 3.5.2 节中描述的流过程 `integers-starting-from`,但有一个重要区别:流过程返回的是一个表示从 `n` 开始的所有整数序列的对象,而 `amb` 过程返回的是单个整数。

Abstractly, we can imagine that evaluating an amb expression causes time

to split into branches, where the computation continues on each branch with one

of the possible values of the expression. We say that amb represents a

从抽象层面看,我们可以想象,对一个 amb 表达式求值会导致时间分裂成若干分支,计算在每条分支上分别以该表达式的某个可能值继续推进。我们说 amb 代表一个

nondeterministic choice point. If we had a machine with a sufficient

number of processors that could be dynamically allocated, we could implement

the search in a straightforward way. Execution would proceed as in a

sequential machine, until an amb expression is encountered. At this

point, more processors would be allocated and initialized to continue all of

the parallel executions implied by the choice. Each processor would proceed

sequentially as if it were the only choice, until it either terminates by

encountering a failure, or it further subdivides, or it finishes.

非确定性选择点 (nondeterministic choice point)。如果我们拥有一台配备足够多可动态分配处理器的机器,便可以直接实现这种搜索。执行过程就像在顺序机器上一样推进,直到遇到一个 amb 表达式。此时,系统分配并初始化更多处理器,以继续由该选择所隐含的所有并行执行路径。每个处理器都像自己是唯一的选择一样顺序推进,直到它因遇到失败而终止、或进一步细分、或执行完毕。

On the other hand, if we have a machine that can execute only one process (or a

few concurrent processes), we must consider the alternatives sequentially. One

could imagine modifying an evaluator to pick at random a branch to follow

whenever it encounters a choice point. Random choice, however, can easily lead

to failing values. We might try running the evaluator over and over, making

random choices and hoping to find a non-failing value, but it is better to

另一方面,如果我们只有一台只能执行单个计算过程(或少数几个并发计算过程)的机器,就必须顺序地考虑各种选择。可以想象对求值器加以改造,使它在遇到选择点时随机选取一条分支。然而,随机选择很容易导致失败的值。我们可以反复运行求值器,随机做出选择并期望找到一个不失败的值,但更好的做法是

systematically search all possible execution paths. The amb

evaluator that we will develop and work with in this section implements a

systematic search as follows: When the evaluator encounters an application of

amb, it initially selects the first alternative. This selection may

itself lead to a further choice. The evaluator will always initially choose

the first alternative at each choice point. If a choice results in a failure,

then the evaluator automagically

系统地搜索所有可能的执行路径。我们将在本节开发并使用的 amb 求值器实现了如下系统化搜索:当求值器遇到一个 amb 的应用时,它首先选取第一个备选项。这一选择本身可能又导致进一步的选择。求值器在每个选择点上总是优先选取第一个备选项。若某次选择导致失败,求值器便会自动地 (automagically)

backtracks to the most

recent choice point and tries the next alternative. If it runs out of

alternatives at any choice point, the evaluator will back up to the previous

choice point and resume from there. This process leads to a search strategy

known as

depth-first search or

chronological backtracking.

Subheading: Driver loop

回溯到最近的选择点,并尝试下一个备选项。如果在某个选择点上所有备选项都已耗尽,求值器就退回到上一个选择点并从那里继续。这一过程形成了一种被称为深度优先搜索 (depth-first search) 或按时序回溯 (chronological backtracking) 的搜索策略。

小节标题:驱动循环

The driver loop for the amb evaluator has some unusual properties. It

reads an expression and prints the value of the first non-failing execution, as

in the prime-sum-pair example shown above. If we want to see the value

of the next successful execution, we can ask the interpreter to backtrack and

attempt to generate a second non-failing execution. This is signaled by typing

the symbol try-again. If any expression except try-again is

given, the interpreter will start a new problem, discarding the unexplored

alternatives in the previous problem. Here is a sample interaction:

amb 求值器的驱动循环具有一些不寻常的特性。它读入一个表达式并打印第一次无失败执行的值,正如上面所示的 prime-sum-pair 示例那样。如果想查看下一次成功执行的值,可以要求解释器回溯并尝试产生第二次无失败执行。这通过输入符号 try-again 来触发。若给出 try-again 之外的任何表达式,解释器就会开始处理一个新问题,并丢弃前一个问题中尚未探索的备选项。下面是一段交互示例:

Racket #lang sicp
(amb ⟨e₁⟩ ⟨e₂⟩ … ⟨eₙ⟩)
Racket #lang sicp
(list (amb 1 2 3) (amb 'a 'b))
Racket #lang sicp
(1 a) (1 b) (2 a) (2 b) (3 a) (3 b)
Racket #lang sicp
(define (require p)
 (if (not p) (amb)))
Racket #lang sicp
(define (an-element-of items)
 (require (not (null? items)))
 (amb (car items)
 (an-element-of (cdr items))))
Racket #lang sicp
(define (an-integer-starting-from n)
 (amb n (an-integer-starting-from (+ n 1))))
Racket #lang sicp
;;; Amb-Eval input:
(prime-sum-pair '(1 3 5 8) '(20 35 110))

;;; Starting a new problem
;;; Amb-Eval value:
(3 20)

;;; Amb-Eval input:
try-again

;;; Amb-Eval value:
(3 110)

;;; Amb-Eval input:
try-again

;;; Amb-Eval value:
(8 35)

;;; Amb-Eval input:
try-again

;;; There are no more values of
(prime-sum-pair
 (quote (1 3 5 8))
 (quote (20 35 110)))

;;; Amb-Eval input:
(prime-sum-pair '(19 27 30) '(11 36 58))

;;; Starting a new problem
;;; Amb-Eval value:
(30 11)