在维基百科上,它说使用 call/cc 您可以实现 amb 运算符以进行非确定性选择,我的问题是您将如何以一种语言实现 amb 运算符,在这种语言中,对延续的唯一支持是以延续传递样式编写,例如二郎?
1 回答
如果您可以将构成成功解决方案或选择的约束编码为警卫,则可以使用列表推导来生成解决方案。例如,列表理解文档显示了解决毕达哥拉斯三元组的示例,这是一个经常使用解决的问题amb
(参见例如SICP 的练习 4.35,第 2 版)。这是列表推导页面上显示的更有效的解决方案pyth1/1
:
pyth1(N) ->
[ {A,B,C} ||
A <- lists:seq(1,N-2),
B <- lists:seq(A+1,N-1),
C <- lists:seq(B+1,N),
A+B+C =< N,
A*A+B*B == C*C
].
的一个重要方面amb
是有效地搜索解决方案空间,这是通过为 、 和 with 生成可能的值来完成的A
,B
然后C
使用lists:seq/2
守卫约束和测试这些值。请注意,该页面还显示了一个效率较低的解决方案,名为pyth/1
where A
、B
和 ,C
它们都是使用lists:seq(1,N)
;生成的。该方法会生成所有排列,但比 慢pyth1/1
(例如,在我的机器上,pyth(50)
比 慢 5-6 倍pyth1(50)
)。
如果您的约束不能表示为警卫,您可以使用模式匹配和 try/catch 来处理失败的解决方案。例如,这里是pyth/1
重写为常规函数triples/1
和递归的相同算法triples/5
:
-module(pyth).
-export([triples/1]).
triples(N) ->
triples(1,1,1,N,[]).
triples(N,N,N,N,Acc) ->
lists:reverse(Acc);
triples(N,N,C,N,Acc) ->
triples(1,1,C+1,N,Acc);
triples(N,B,C,N,Acc) ->
triples(1,B+1,C,N,Acc);
triples(A,B,C,N,Acc) ->
NewAcc = try
true = A+B+C =< N,
true = A*A+B*B == C*C,
[{A,B,C}|Acc]
catch
error:{badmatch,false} ->
Acc
end,
triples(A+1,B,C,N,NewAcc).
我们使用模式匹配有两个目的:
- 在函数头中,控制 的值
A
,B
以及C
关于N
并知道我们何时完成 - 在 的最后一个子句的主体中
triples/5
,断言条件A+B+C =< N
和A*A+B*B == C*C
匹配true
如果两个条件true
在 的最后一个子句中匹配triples/5
,我们将解决方案插入我们的累加器列表,但如果任何一个不匹配,我们就会捕获badmatch
错误并保留原始的累加器值。
调用产生与andtriples/1
中使用的列表理解方法相同的结果,但它的速度也是. 即便如此,使用这种方法,任何约束都可以编码为普通函数,并在 try/catch 表达式中测试是否成功。pyth/1
pyth1/1
pyth/1