PROLOG未定义过程ERROR(两个参数递归)

时间:2016-06-03 11:34:08

标签: list recursion prolog clpfd clpb

count([], 0, 0).
count([X|T], M, N) :- 1 is X, count(T, MRec, NRec), 
                              M is MRec, N is NRec+1.

count([X|T], M, N) :- 0 is X, count(T, MRec, NRec), 
                              M is MRec+1, N is NRec.

control_number(L) :- count_digit(L, M, N), 2 is M, 3 is N.


?- control_number([1,1,0,0,1]).
ERROR: count_number/3: Undefined procedure: count/3

各位大家好,我需要帮助。此代码必须递归地提供两个单独数字的计数。但是,我无法提供递归 有2个参数。我猜MRecNRec无论如何都无效。 任何帮助将不胜感激。谢谢你......

3 个答案:

答案 0 :(得分:3)

这是一个更惯用的重写:

count_digits([], 0, 0).
count_digits([1|T], M, N) :-
   count_digits(T, M, NRec),
   N is NRec+1.
count_digits([0|T], M, N) :-
   count_digits(T, MRec, N),
   M is MRec+1.

control_number(L) :-
   count_digits(L, 2, 3).

使用library(clpfd)可以大大改善这一点。也许其他人会回答。

答案 1 :(得分:3)

正如@false已经指出的那样,这个谓词非常适合clpfd。除此之外,我添加了约束(标记为% <-)以确保MN在递归情况下大于0,因此Prolog不会继续搜索解决方案一旦这些变量减少到0

:- use_module(library(clpfd)).

count_digits([], 0, 0).
count_digits([1|T], M, N) :-
   N #> 0,                        % <-
   NRec #= N-1,
   count_digits(T, M, NRec).
count_digits([0|T], M, N) :-
   M #> 0,                        % <-
   MRec #= M-1,
   count_digits(T, MRec, N).

通过这些微小修改,您已经可以通过多种方式使用count_digits / 3。例如,要求所有列表包含2个0和3个1&#39>:

   ?- count_digits(L,2,3).
L = [1,1,1,0,0] ? ;
L = [1,1,0,1,0] ? ;
L = [1,1,0,0,1] ? ;
L = [1,0,1,1,0] ? ;
L = [1,0,1,0,1] ? ;
L = [1,0,0,1,1] ? ;
L = [0,1,1,1,0] ? ;
L = [0,1,1,0,1] ? ;
L = [0,1,0,1,1] ? ;
L = [0,0,1,1,1] ? ;
no

或计算给定列表中01&s的出现次数:

   ?- count_digits([1,1,0,0,1],M,N).
M = 2,
N = 3
% 1

甚至可以在包含变量的列表中询问01的数量:

   ?- count_digits([1,0,X,Y],M,N).
M = X = Y = 1,
N = 3 ? ;
M = N = 2,
X = 1,
Y = 0 ? ;
M = N = 2,
X = 0,
Y = 1 ? ;
M = 3,
N = 1,
X = Y = 0

这已经非常好了,有人可能会对谓词满意。如果您打算像@false所建议的那样将它与control_number / 1一起使用,那当然没问题。然而,在其他一些问题上愚弄它可能是值得的。例如,最常见的查询:M 0&{39}和N 1&#39;是哪些列表?

   ?- count_digits(L,M,N).
L = [],
M = N = 0 ? ;
L = [1],
M = 0,
N = 1 ? ;
L = [1,1],
M = 0,
N = 2 ? ;
L = [1,1,1],
M = 0,
N = 3 ?
...

仅生成仅由1组成的列表。这是因为第一个递归规则是描述1作为列表的第一个元素的情况的规则。所以解决方案的顺序是不公平的。以下查询会发生什么甚至可能不那么直观:哪些列表具有相同(但不是固定)的0&{39}和1的数量:< / p>

   ?- count_digits(L,M,M).
L = [],
M = 0 ? ;

有一个答案然后谓词循环。这不是一个理想的财产。关于这个查询的有趣观察:如果在具有固定长度的列表上使用它,结果实际上是预期的:

   ?- length(L,_), count_digits(L,M,M).
L = [],
M = 0 ? ;
L = [1,0],
M = 1 ? ;
L = [0,1],
M = 1 ? ;
L = [1,1,0,0],
M = 2 ? ;
L = [1,0,1,0],
M = 2 ? ;
...

将此想法应用于先前的查询会产生对结果的公平排序:

   ?- length(L,_), count_digits(L,M,N).
L = [],
M = N = 0 ? ;
L = [1],
M = 0,
N = 1 ? ;
L = [0],
M = 1,
N = 0 ? ;
L = [1,1],
M = 0,
N = 2 ? ;
L = [1,0],
M = N = 1 ? ;
...

在不必为辅助目标添加前缀的情况下获得这些结果肯定会很好。再仔细看一下count_digits / 3所描述的关系,另一个观察就会出现:如果有M 0&{39}和N 1&# 39; s列表的长度实际上是固定的,即M + N。要将这些观察结果用于工作,可以将count_digits / 3重命名为list_0s_1s / 3,并将count_digits / 3重新定义为具有以下约束的调用谓词:

:- use_module(library(clpfd)).

count_digits(L,M,N) :-
   X #= M+N,
   length(L,X),               % L is of length M+N
   list_0s_1s(L,M,N).

list_0s_1s([], 0, 0).
list_0s_1s([1|T], M, N) :-
   N #> 0,
   NRec #= N-1,
   list_0s_1s(T, M, NRec).
list_0s_1s([0|T], M, N) :-
   M #> 0,
   MRec #= M-1,
   list_0s_1s(T, MRec, N).

上面的前三个查询产生与以前相同的结果,但这两个查询现在以公平的顺序生成结果而没有循环:

   ?- count_digits(L,M,N).
L = [],
M = N = 0 ? ;
L = [1],
M = 0,
N = 1 ? ;
L = [0],
M = 1,
N = 0 ? ;
L = [1,1],
M = 0,
N = 2 ? ;
L = [1,0],
M = N = 1 ? 
...

   ?- count_digits(L,M,M).
L = [],
M = 0 ? ;
L = [1,0],
M = 1 ? ;
L = [0,1],
M = 1 ? ;
L = [1,1,0,0],
M = 2 ? ;
L = [1,0,1,0],
M = 2 ? 
...

关于谓词control_number / 1的最后两个注释:首先,如果你使用的是/ 2,请确保使用它如下:

   ?- M is 2.
M = 2
% 1

而不是(在control_number / 1的定义中使用):

   ?- 2 is M.
     ERROR!!
     INSTANTIATION ERROR- in arithmetic: expected bound value
% 1

其次,如果您打算使用类似control_number / 1的谓词来调用count_digits / 3,请不要在之后设置M is 2N is 3 之类的目标count_digits / 3的实际调用。这样你就会要求count_digits(L,M,N)的所有解决方案,其中包含无限多的解决方案,然后在后续目标中过滤掉满足约束的解决方案(M is 2N is 3 )。通过这种目标排序,您可以确保在生成有限数量的解决方案后control_number / 1不会终止,因为第一个目标会产生无限多个解决方案候选者,这些目标随后会根据您的约束而失败。相反,首先放置这些约束,或者将它们直接作为参数放入@false发布的目标中。

答案 2 :(得分:0)

累积参数是要走的路(你需要一个辅助谓词来初始化这些参数):

count(List,C0,C1) :-
    count_aux(List,C0,C1,0,0).

count_aux([],C0,C1,C0,C1).
count_aux([0|Rest],C0,C1,PartialC0,PartialC1) :-
    IncC0 is PartialC0+1,
    !,
    count_aux(Rest,C0,C1,IncC0,PartialC1).
count_aux([1|Rest],C0,C1,PartialC0,PartialC1) :-
    IncC1 is PartialC1+1,
    !,
    count_aux(Rest,C0,C1,PartialC0,IncC1).
count_aux([_|Rest],C0,C1,PartialC0,PartialC1) :-
    count_aux(Rest,C0,C1,PartialC0,PartialC1).

注意:

  • 您应该拨打count / 3,而不是count_aux / 5.
  • count_aux / 5的最后两个参数是累积参数 初始化为零。
  • count_aux / 5的第一个子句是累积的基本情况 返回参数。
  • count_aux / 5的最后一个子句可防止列表项中的谓词失败 不是0也不是1.

示例:

?- count([1,1,0,0,0,k],A,B).
A = 3,
B = 2.