将Haskell翻译为Prolog - 查找sum的子列表

时间:2017-04-10 20:43:26

标签: haskell prolog list-comprehension sublist prolog-findall

我正在尝试翻译以下Haskell代码:

-- sublistSums list sum returns a list of the sublists of list that add up to sum
sublistSums :: [Int] -> Int -> [[Int]]
sublistSums [] 0 = [[]]
sublistSums [] _ = []
sublistSums (x:xs) sum = [x:tailList | tailList <- sublistSums xs (sum-x)] ++
                         sublistSums xs sum

...进入Prolog。该代码应该给我一个列表,列出其总和加起来的目标号码。例如,

?- sublistSums([[1,2,3],[4,5,6]],6).

如果因为[[1,2,3]]

而绑定,应该给你1+2+3=6.

这是我到目前为止所做的:

sublistSums([], 0) :-
    sublistSums([],0,[]).
sublistSums([], _) :-
    sublistSums([],0,[]).
sublistSums([x|xs], sum) :-
    conc(conc(x,findall(xs,(sublistSums(xs, (sum-x))),List)), sublistSums(xs, sum)).

然而,当我调用sublistSums函数时,它给了我:

?- sublistSums([[1,2,3],[4,5,6]],6).
false.

我也尝试过:

sublistSums([], 0, ReList) :-
   [[]].
sublistSums([], _, ReList) :-
   [].
sublistSums([x|xs], sum, ReList) :-
   conc(conc(x,findall(xs,(sublistSums(xs, (sum-x)),ReList),List)), sublistSums(xs, sum, ReList)).

仍然给我相同的结果。不完全确定我在这里做错了什么。

1 个答案:

答案 0 :(得分:4)

您的Prolog代码中存在一些基本问题:

  • sum是Prolog atom ,因此永远不会与列表统一。
  • 在Prolog中,我们在实体之间定义关系,您需要谓词参数来引用这些实体。

因此,您无法翻译代码 verbatim 。如果您例如引入一个新的参数来保存原始函数的返回值,那么仍然可以使用完全字面的Haskell→Prolog转换。你的第二个片段是朝这个方向发展的,但它并不是独立的,也包含几个错误。例如,xssum atoms ,而Prolog 变量以大写字母或下划线开头。

此外,如果您继续尝试文字翻译,您将放弃真正关系的普遍性中受益的机会。典型的Prolog谓词不仅可以在 one 中使用,而且可以在几个方向中使用,并且您不仅可以将它们用于计算,还可以使用它们测试完成部分结果。

因此,我想向您展示一个更强大的惯用 Prolog解决方案,而不是字面翻译。

我们的第一个观察结果,从类型签名中可以清楚地看出:我们推理整数。因此,我建议您使用Prolog系统的 CLP(FD)约束进行完全声明性整数运算。有关此功能的详情,请参阅

其次,该任务可以被视为“过滤”列表,为此,我们使用library(reif)提供的tfilter/3

以下是完整的Prolog解决方案:

sublist_sum(Lss0, Sum, Lss) :-
        tfilter(sum_intlist(Sum), Lss0, Lss).

sum_intlist(Sum, Ls, T)  :-
        sum(Ls, #=, Sum0),
        zcompare(C, Sum0, Sum),
        eq_truth(C, T).

eq_truth(=, true).
eq_truth(<, false).
eq_truth(>, false).

将此谓词视为关联列表Lss0 sum 和第二个列表Lss,以便您声明所有要求是满意的。请注意,我没有说这些参数中的哪一个是“给定的”,因为它们中的任何一个都可能完全,部分或完全没有指定。

您的示例按要求运行:

?- sublist_sum([[1,2,3],[4,5,6]], 6, Ls).
Ls = [[1, 2, 3]].

此示例保持在原始函数的范围内:第一个和第二个参数已完全指定,第三个参数用于表示“结果”。

然而,正如Prolog的典型情况一样,我们还可以进行更多一般查询,其中不仅“结果”,还有其他部分未指定。显然,这远远超出了函数的范围,原始代码的字面翻译可能没有这个好处。

例如:

?- sublist_sum([[1,2,N]], 6, Ls).

在这种情况下,我们已离开N 未指定,并要求Prolog考虑可能适用于N所有案例。

作为回应,Prolog 生成不同的可能性,以分离表示:

N = 3,
Ls = [[1, 2, 3]] ;
Ls = [],
N in inf..2,
-3+_10694#=N,
_10694 in inf..5 ;
Ls = [],
N in 4..sup,
-3+_10662#=N,
_10662 in 7..sup.

请注意Ls取决于N

我们还可以进一步概括进一步,甚至不指定总和

?- sublist_sum([[A,B,C]], Sum, Ls).
Ls = [[A, B, C]],
A+B+C#=Sum ;
Ls = [],
A+B+C#=_15806,
_15806#=<Sum+ -1,
zcompare(<, _15806, Sum) ;
Ls = [],
A+B+C#=_15806,
Sum#=<_15806+ -1,
zcompare(>, _15806, Sum).

再次,Prolog列举了不同的案例。

此外,我们可以专门化查询并将其用于测试关系:

?- sublist_sum([[1,2,3]], 5, []).
true.

在这里,我们已经问过:这个具体案例是否持有?,Prolog以true回复,表明该关系完全符合预期情况下。