【问题标题】:PROLOG Undefined procedure ERROR (Two parameters recursion)PROLOG Undefined procedure ERROR(两个参数递归)
【发布时间】:2016-06-03 11:34:07
【问题描述】:
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 无论如何都是无效的。 任何帮助将不胜感激。现在谢谢...

【问题讨论】:

  • count_digit 还是count
  • 它必须是 count_digit。我看不到那个错误。现在代码是有效的。谢谢你:)
  • 您可以使用内置谓词和库谓词,还是自己编写代码来处理递归?

标签: list recursion prolog clpfd clpb


【解决方案1】:

这是一个更惯用的重写:

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) 可以大大改善这一点。也许其他人会回答。

【讨论】:

    【解决方案2】:

    正如@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 的列表:

       ?- 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 的出现次数:

       ?- 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 一起使用,那当然没问题。然而,花时间在一些其他的查询上花点时间可能是值得的。例如最一般的查询:M0's 和 N1'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 作为列表的第一个元素的情况的规则。因此,解决方案的顺序不公平。以下查询的结果可能更不直观:哪些列表具有相同(但不固定)数量的01

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

    【讨论】:

    • @mat: 是的,一个小小的括号移位可以将整个句子移向语法正确的领域,这真是太神奇了,不是吗?感谢您的荣誉:-D
    【解决方案3】:

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

    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.
    

    【讨论】:

    • 相当易碎!该代码仅在 sufficnet 的“广告宣传”中有效。目标 count([0,0,1],2,1) 成功(如预期),但更一般的目标 count([_,_,_],2,1) 失败!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-26
    • 2011-12-09
    相关资源
    最近更新 更多