在序言中找到列表的功能

时间:2020-05-27 11:05:50

标签: prolog

我是Prolog的新手,我正在尝试编写一个函数,该函数查找遵循某些规则的列表。 更具体地说,给定两个数字N和K,我想让我的函数找到一个列表,该列表具有K个2的幂,它们的和为N。该列表不能包含每个幂,而是每个幂的总和。例如,如果N = 13并且K = 5,我希望我的列表为[2,2,1],其中前2个表示两个4,第二个2表示两个2,第三个1表示一个1(4 + 4 + 2 + 2 + 1 = 13)。考虑从列表的末尾开始,每个位置i代表2的2 ^ i次方。因此,我编写了以下代码:

sum2(List, SUM, N) :-
   List = []  -> N=SUM;
   List = [H|T],
   length(T, L),
   NewSUM is SUM + (H * 2**L),
   sum2(T, NewSUM, N).

powers2(N,K,X):-
   sum2(X,0,N),
   sum_list(X, L),
   K = L.

问题是:

?- sum2([2,2,1],0,13).
true.
?- sum2([2,2,1],0,X).
X = 13.
?- sum2(X,0,13).
false.
?- powers2(X,5,[2,2,1]).
X = 13.
?- powers2(13,5,[2,2,1]).
true.
?- powers2(13,X,[2,2,1]).
X = 5.
?- powers2(13,5,X).
false.

在这种情况下,X代表列表,我希望输出是遵循规则的列表,而不是false。您能帮我找到解决方法,并在这些情况下提供输出列表吗?

1 个答案:

答案 0 :(得分:0)

带有未绑定列表的谓词失败的直接原因是由于您将->构造用于控制流。

这是您要执行的操作的简化版本,这是一个用于检查列表是否为空的小谓词:

empty_or_not(List, Answer) :-
    (   List = []
    ->  Answer = empty
    ;   List = [H|T],
        Answer = head_tail(H, T) ).

(附带说明:确切的布局取决于您的口味,但是如果您使用;运算符,则应始终使用括号将代码括起来。我也敦促您切勿;放在行尾,而是放在真正突出的位置。在Prolog中,使用;确实是例外情况,并且其格式与,,很难看到它是否存在,以及它适用于该子句的哪一部分。)

这似乎可行,对吧?

?- empty_or_not([], Answer).
Answer = empty.

?- empty_or_not([1, 2, 3], Answer).
Answer = head_tail(1, [2, 3]).

到目前为止,很好,但是如果我们用一个未绑定的列表来称呼它呢?

?- empty_or_not(List, Answer).
List = [],
Answer = empty.

尽管我们从上面知道,非空列表也很好,但是突然只有空列表被接受。

这是因为->一旦发现其条件已满足,就会删除所有替代项。在最后一个示例中,List是变量,因此无法与[]一起使用。条件List = []将成功(将List绑定到[]),并且将不尝试替代项List = [H|T]。看起来很简单,但是->实际上是Prolog的高级功能。仅应由经验丰富的用户使用,他们知道他们确实真的不需要探索替代方法。

在Prolog中实现析取的通常且通常正确的方法是针对不同的情况使用单独的子句:

empty_or_not([], empty).
empty_or_not([H|T], head_tail(H, T)).

现在,它的行为符合逻辑:

?- empty_or_not([], Answer).
Answer = empty.

?- empty_or_not([1, 2, 3], Answer).
Answer = head_tail(1, [2, 3]).

?- empty_or_not(List, Answer).
List = [],
Answer = empty ;
List = [_2040|_2042],
Answer = head_tail(_2040, _2042).

因此,您对sum2的定义应如下所示:

sum2([], SUM, N) :-
    N = SUM.
sum2([H|T], SUM, N) :-
    length(T, L),
    NewSUM is SUM + (H * 2**L),
    sum2(T, NewSUM, N).

但这只是一小步:

?- sum2(X, 0, 13).
ERROR: Arguments are not sufficiently instantiated
ERROR: In:
ERROR:    [9] _2416 is 0+_2428* ...
ERROR:    [8] sum2([_2462],0,13) at /home/gergo/sum.pl:5
ERROR:    [7] <user>

您正在尝试对H进行算术运算,该运算没有价值。如果要使用“普通” Prolog算术,则在尝试对其进行算术之前,需要枚举H可能具有的适当值。另外,您可以使用算术约束。在Arithmetics in Prolog, represent a number using powers of 2上查看两者的可能实现。

相关问题