0
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 个参数的递归。我想MRec并且NRec在任何情况下都是无效的。任何帮助将不胜感激。现在谢谢...

4

3 回答 3

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 了。例如,询问所有带有 20和 31的列表:

   ?- 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

或者计算给定列表中0' 和' 的出现次数:1

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

甚至在包含变量的列表中询问0' 和' 的数量:1

   ?- 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's 和N 1's 有哪些列表?

   ?- 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's 和1's:

   ?- 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's 和N 1'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 的最后两个注释:首先,如果您使用的是 is/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 2andN is 3 类的目标放在实际调用 count_digits/3 之后。这样,您就要求 的所有解决方案count_digits(L,M,N),其中有无限多个,然后在后续目标中过滤掉满足您的约束(M is 2N is 3)的目标。通过目标的这种排序,您可以确保 control_number/1 在产生有限数量的解决方案后不会终止,因为第一个目标会产生无限多的解决方案候选者,这些目标随后会根据您的约束而失败。相反,首先放置此类约束或将它们直接作为参数放入@false 发布的目标中。

于 2016-06-05T10:41:57.343 回答
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). 也许别人会回答。

于 2016-06-03T13:11:43.137 回答
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 的第一个子句是基本情况,返回累积的参数。
  • 如果列表项不是 0 也不是 1,则 count_aux/5 的最后一个子句可防止谓词失败。

例子:

?- count([1,1,0,0,0,k],A,B).
A = 3,
B = 2.
于 2016-06-08T11:36:24.060 回答