在维基百科上,它说使用call / cc你可以实现amb运算符用于非确定性选择,我的问题是你如何用一种语言来实现amb运算符,其中对continuation的唯一支持是以continuation传递样式写入,就像在erlang?
答案 0 :(得分:3)
如果您可以将构成成功解决方案或选择的约束编码为警卫,则可以使用列表推导来生成解决方案。例如,list comprehension documentation显示了解决Pythagorean triples的示例,这是使用amb
经常解决的问题(请参阅例如exercise 4.35 of SICP, 2nd edition)。以下是列表推导页面上显示的效率更高的解决方案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
的一个重要方面是有效地搜索解空间,这是通过为A
,B
和C
生成{{1}的可能值来完成的。然后使用警卫约束和测试这些值。请注意,该页面还会显示效率较低的解决方案lists:seq/2
,其中pyth/1
,A
和B
都使用C
生成相同的内容;该方法生成所有排列但速度低于lists:seq(1,N)
(例如,在我的计算机上,pyth1/1
比pyth(50)
慢5-6倍。
如果您的约束不能表示为警卫,您可以使用模式匹配和try / catch来处理失败的解决方案。例如,pyth1(50)
重写为常规函数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
会产生与triples/1
和pyth/1
中使用的列表推导方法相同的结果,但它的速度也只有pyth1/1
的一半。即便如此,使用这种方法,任何约束都可以编码为正常函数,并在try / catch表达式中测试成功。