一个带有列表并重复每个元素X次的谓词 - Prolog

时间:2018-04-28 16:41:42

标签: recursion prolog

我可以得到结果为真,但当我试图让它变为假时,我觉得它是无限循环。

repeat(L,N,Result):-
    rHelp(L,N,[],Result).

rHelp(_,_,Result,Result).
rHelp([H|T],N,L1,L2):-
    dupe(H,N,[],L3),
    append(L1,L3,L4),
    rHelp(T,N,L4,L2).

dupe(_,0,L,L).
dupe(H,N,L,Result):-
    N1 is N-1,
    append(L,[H],L1),
    dupe(H,N1,L1,Result).

示例测试:

repeat( [a, b, c], 2, [a, a, b, b, c, c] )
repeat( [1, a, 2, b], 0, [ ] )
repeat( [1, 1, 2], 3, [1, 1, 1, 1, 1, 1, 2, 2, 2] )

都是真的。我只是试图得到一个错误的结果。

1 个答案:

答案 0 :(得分:0)

使程序失败的一个原因是它描述了太多不正确的解决方案:

?- repeat([a, b, c], 2, Result).
Result = [] ;
Result = [a, a] ;
Result = [a, a, b, b] ;
Result = [a, a, b, b, c, c] ;
% nontermination

您接受太多解决方案的原因是rHelp/4的第一个条款:

rHelp(_,_,Result,Result).

在这里你说任何输入列表,在你有中间结果Result的计算中的任何一点,这是一个正确的解决方案。但这种情况并非如此。一旦用尽整个输入列表,中间结果只是一个完整的结果。该条款应为:

rHelp([], N, Result, Result).

请注意,我发现这主要是通过模式匹配。相邻的子句如

foo(_, Bar).
foo([H|T], Bar) :- ...

只是不正确。为什么_不是空列表?在大多数情况下,像这样的谓词在头部会有相互排斥的模式,而事实并非如此。

有了这个问题,我们可以再次尝试测试:

?- repeat([a, b, c], 2, Result).
Result = [a, a, b, b, c, c] ;
% nontermination

更好!但它仍然在寻找更多的解决方案,尽管没有。这也是条款不相互排斥的情况:

dupe(_,0,L,L).
dupe(H,N,L,Result):-
    N1 is N-1,
    ...

如果第二个参数不是0,则第二个条款适用。如果第二个参数是0,则第一个子句适用......但第二个参数也适用!在使用第一个子句找到解决方案后,Prolog将使用N = 0回溯并执行第二个子句。然后N1将变为-1,并且您的程序将以递归方式查找负无穷大。

修复是在第二个子句中添加一个保护N > 0。通过这两个更改,测试可以按预期工作:

?- repeat([a, b, c], 2, Result).
Result = [a, a, b, b, c, c] ;
false.

这里要注意的一个重点是,通过使用不太具体的查询,更容易理解谓词的行为。无需为Result参数指定固定列表:保持免费并查看Prolog为您提供的内容!