Erlang实现了amb运营商。

时间:2015-07-07 19:51:31

标签: erlang continuations continuation-passing

在维基百科上,它说使用call / cc你可以实现amb运算符用于非确定性选择,我的问题是你如何用一种语言来实现amb运算符,其中对continuation的唯一支持是以continuation传递样式写入,就像在erlang?

1 个答案:

答案 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的一个重要方面是有效地搜索解空间,这是通过为ABC生成{{1}的可能值来完成的。然后使用警卫约束和测试这些值。请注意,该页面还会显示效率较低的解决方案lists:seq/2,其中pyth/1AB都使用C生成相同的内容;该方法生成所有排列但速度低于lists:seq(1,N)(例如,在我的计算机上,pyth1/1pyth(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). AB相对于C的值并知道我们何时完成
  • N的最后一个条款的正文中,断言条件triples/5A+B+C =< N匹配A*A+B*B == C*C

如果两个条件都匹配true的最后一个句子中的true,我们会将解决方案插入到累加器列表中,但如果两者无法匹配,我们会捕获triples/5错误并保持原始累加器值。

调用badmatch会产生与triples/1pyth/1中使用的列表推导方法相同的结果,但它的速度也只有pyth1/1的一半。即便如此,使用这种方法,任何约束都可以编码为正常函数,并在try / catch表达式中测试成功。