【问题标题】:Counting occurrences in list计算列表中的出现次数
【发布时间】:2016-05-27 11:08:08
【问题描述】:

我正在尝试创建一个规则来计算给定列表中某个元素的出现次数,到目前为止我所尝试的似乎并没有按我预期的方式工作:

这里的第一个参数应该是列表,第二个是我们要查找的元素,最后一个是出现次数:

%empty list should always return 0 occurences
count([],E,0) :- true.

%if our head is what we are looking for, count
count([E|T],E,N) :- count(T,E,N-1).

%otherwise, do not count
count([H|T],E,N) :- H \== E, count(T,E,N).

这里H是给定列表的head,T是tail。

基本情况,例如count([],1,N). 按预期返回N = 0,但只要列表不为空,我们总是会得到false.:

?- count([1],1,N).
false.
?- count([1,2,1,3],1,N).
false.

谁能指出我做错了什么?

更新:

用

替换第二行似乎有效
count([E|T],E,N+1) :- count(T,E,N).

但我就是想不通为什么这不等于我的第一个想法。

然后我们得到

?- count([1,2,1,3],1,N).
N = 0+1+1 

这是正确的。

【问题讨论】:

  • 谢谢。我确实找到了那个答案,但我仍然不明白为什么我的 not 工作。
  • 别忘了:count(L,E,C) :- aggregate(count,member(E,L),C).

标签: list count prolog clpfd prolog-dif


【解决方案1】:

评估与统一

在 Prolog 中 =/2 是一个 unification 运算符。它不会将术语作为表达式进行评估,即使该术语可能代表数值上可评估的东西。同样,当您使用 count([E|T], E, N+1) 时,Prolog 不会评估术语 N+1。对于 Prolog,这只是另一个术语,内部表示为 +(N, 1)。

对于要作为数值表达式解释和评估的术语,您需要使用特定的 Prolog 运算符来执行此操作。正如@SQB 指出的那样,其中之一是is/2:

N = 1, N1 is N + 1.

这将导致N1 = 2。然而这个:

N = 1, N1 = N + 1.

将导致:N1 = 1 + 1(或等效的 N1 = +(1, 1))。

Prolog 中也有数值比较运算符,它们也可以计算表达式。这些是=:=/2、>/2、>=/2、</2 和 =</2。所以你会看到以下内容:

1 + 2 =:= 3.

将产生“真”,因为=:=/2 专门用于比较可计算数值表达式的相等性。然而:

1 + 2 = 3.

将产生“false”,因为术语 +(1,2) 与术语 3 不匹配(或者更准确地说,不能统一)。

啊!参数没有充分实例化!

我在 SO 上看到了很多关于他们的论点“没有充分实例化”的错误的帖子。其中许多都在使用is/2。如上所述,is/2 将评估第二个参数中的表达式,然后将该结果与第一个参数统一。第二个参数必须是完全可评估的(表达式中涉及的所有变量都必须用数值​​实例化)否则你会得到一个错误。同样,在使用表达式比较时,两个参数中的所有变量都必须完全实例化。因此,如果X 是一个未绑定的变量,以下将产生“参数没有充分实例化”错误:

9 is X * 3.       % Will NOT yield X = 3, but will give an error
Y + 2 =:= X * 2.  % Error if either X or Y are not instantiated
Y = 1, X = 2, Y + 2 =:= X * 2.  % Yields "false" (fails) since 3 is not equal to 4
Y = 1, X = 2, Y + 2 < X * 2.    % Yields "true" (succeeds) since 3 is less than 4
Y = 1, X = 2, X + 1 = Y + 2.    % Yields "false" since +(2,1) doesn't unify with +(1,2)

对表达式执行约束逻辑时,使用的工具是 CLP(FD) 库。因此:

X * 3 #= 6.

将屈服,X = 2。

【讨论】:

    【解决方案2】:

    问题是N+1(或N-1)没有被评估,正如您的第二个(工作)示例所见。

    % empty list has 0 occurrences
    count([], _, 0).
    
    % if our head is what we are looking for, count
    count([E|T], E, N) :-
        N_1 is N - 1,         % this is the important one
        count(T, E, N_1).
    
    % otherwise, do not count
    count([H|T], E, N) :-
        H \== E,
        count(T, E, N).
    

    is 实际上评估 等式,而不是统一在您下次与N-1 通话中的N。这就是为什么在您的第二个示例中,您最终使用 N=0+1+1 而不是 N=2。

    【讨论】:

    • 感谢您的回答。我仍然无法理解为什么 N+1 有效,而 N-1 示例却无效,这两个示例对我来说似乎表达了同样的事情,是吗?跨度>
    • 因为在第二个示例中,N 不会统一,直到您击中 0,之后在遍历树时,所有 +1 都“粘在”上。在第一个示例中,-1 已经存在,因此您的下一个 N 是“N-1”、“N-1-1”等等,它永远不会与 0 统一。我稍后会将其编辑到我的答案中.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多