将Haskell转换为Prolog-查找和的子列表

将Haskell转换为Prolog-查找和的子列表,haskell,prolog,list-comprehension,sublist,prolog-findall,clpfd,Haskell,Prolog,List Comprehension,Sublist,Prolog Findall,Clpfd,我正在尝试翻译以下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 <- sub

我正在尝试翻译以下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
如果由于
1+2+3=6而绑定,则应为您提供
[[1,2,3]]

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

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)).

仍然给我同样的结果。不太清楚我到底做错了什么

Prolog代码中有几个基本问题:

  • sum
    是一个Prologatom,因此它永远不会与列表统一
  • 在Prolog中,我们定义了实体之间的关系,并且需要谓词参数来引用这些实体
因此,不能逐字翻译代码。不过,一个相当字面的Haskell&rightarrow;例如,如果您引入一个新参数来保存原始函数的返回值,那么Prolog翻译是可能的。您的第二个代码段就是朝这个方向发展的,但它不是独立的,而且还包含一些错误。例如,
xs
sum
原子,而Prolog变量以大写字母或小写字母开头

此外,如果您继续尝试直译,您将失去真正受益于关系的普遍性的机会。典型的序言谓词不仅可以在一个方向上使用,还可以在多个方向上使用,您不仅可以使用它们进行计算,还可以使用它们测试和完成部分结果


因此,我想向您展示这个任务的一个更加地道的序言解决方案,而不是直译

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

第二,任务可以被视为“过滤”列表,为此,我们使用提供的
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). 此示例处于原始函数的范围内:第一个和第二个参数完全指定,第三个参数用于表示“结果”

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

例如:

?- sublist_sum([[1,2,N]], 6, Ls). 注意
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). -子列表_sum([[A,B,C]],sum,Ls)。 Ls=[[A,B,C]], A+B+C#=总和; Ls=[], A+B+C=U 15806,
_15806#=为了简单起见,有没有一种方法可以在不使用库(reif)的情况下解决这个问题?如果您想破坏代码的声明性含义,广泛使用的谓词
include/3
是一种很好的方法,因为它只对其中一个分支进行不可加性的提交,即使这两个分支仍然适用。您可以很容易地找到一个实现,也可以自己实现它。生成的代码可能和Haskell版本一样有限。好的,明白了。另外,我不明白为什么sum_intlist(sum)用一个变量调用,而sum_intlist(sum,Ls,t)需要三个变量?我试着运行它,结果它给了我一个错误。对不起,所有的问题。这是一个结束,或者,正如你在Haskell中所说的,部分应用。谓词
tfilter/3
总是使用两个附加参数调用闭包:list元素和truth值,以决定是否必须包含该元素。当然,这两个选项都可能适用,在这种情况下,两个答案都是回溯生成的
tfilter/3
在安全的情况下自动提交到其中一个分支,即由于参数已被充分实例化而无法应用其他分支时。
sum\u intlist
而不是
sum\u intlist\t
? ?- sublist_sum([[1,2,N]], 6, Ls). 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. ?- 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). ?- sublist_sum([[1,2,3]], 5, []). true.