9
counter([],[]).
counter([H|T],[[H,C1]|R]) :- counter(T,[[H,C]|R]),!, C1 is C+1.
counter([H|T],[[H,1]|R]) :- counter(T,R).

“!”的作用是什么?因为我在上面和下面的代码中得到相同的输入输出?

 counter([],[]).
 counter([H|T],[[H,C1]|R]) :- counter(T,[[H,C]|R]),C1 is C+1.
 counter([H|T],[[H,1]|R]) :- counter(T,R).

我是 Prolog 的新手。

4

4 回答 4

5

“!”的作用是什么?

切割修剪搜索空间。也就是说,在一个纯粹且单调的程序中,剪切将删除一些解决方案或答案。只要那些是多余的就可以了。这听起来很无辜和有用,不是吗?我们来看一下!

以免我忘记,使用[E,Nr]来表示对是相当不寻常的,最好使用对E-Nr

我们现在将比较counter_cut/2counter_sans/2

| ?- counter_cut([a,a],Xs).
     Xs = [[a,2]].

| ?- counter_sans([a,a],Xs).
     Xs = [[a, 2]]
  ;  Xs = [[a, 1], [a, 1]].     % <<< surprise !!!

所以剪辑版的解法较少。似乎counter_cut/2保留的解决方案是正确的。在这个非常特殊的情况下。它总是选对的吗?我将尝试一个更通用的查询:

| ?- counter_cut([a,B],Xs).
     B = a,
     Xs = [[a, 2]].

| ?- counter_sans([a,B],Xs).
     B = a,
     Xs = [[a, 2]]
  ;  Xs = [[a, 1], [B, 1]].

再次,_sans更健谈,这一次,它甚至更正确;最后一个答案包括B = b. 换句话说,

| ?- counter_cut([a,B], Xs), B = b.
     fails.              % incomplete !

| ?- counter_sans([a,B], Xs), B = b.
     B = b,
     Xs = [[a,1],[b,1]].

所以有时_cut版本更好,有时_sans. 或者更直接地说:两者都是错误的,但_sans-version 至少包括所有解决方案。

这是一个“纯化”版本,它只是将最后一条规则重写为两种不同的情况:一种用于列表的末尾,另一种用于更进一步的不同元素。

counter_pure([],[]).
counter_pure([H|T],[[H,C1]|R]) :- counter_pure(T,[[H,C]|R]), C1 is C+1.
counter_pure([H],[[H,1]]).
counter_pure([H,D|T],[[H,1]|R]) :- dif(H,D), counter_pure([D|T],R).

从不太著名的效率角度来看。

这是一个具有合理树统一的系统的效率测试用例:

?- Es = [e|Es], counter(Es, Dict).
resource_error(stack).

相反,实现应该平稳循环,至少到这个宇宙的尽头。严格来说,该查询必须产生资源错误,但只有在它计数到比10^100000000.

于 2017-05-09T12:17:54.313 回答
4

这是我纯粹且希望有效的解决方案:

counter([X|L], C):- counter(L, X, 1, C).

counter([],X, Cnt, [[X,Cnt]]).
counter([Y|L], X, Cnt, [[X,Cnt]|C]):-
  dif(X, Y),
  counter(L, Y, 1, C).
counter([X|L],X, Cnt, [[X,XCnt]|C]):-
  Cnt1 #= Cnt+1,
  Cnt1 #=< XCnt,
  counter(L, X, Cnt1, [[X,XCnt]|C]).

按照@falseif_3的建议使用:

counter([X|L], C):- counter(L, X, 1, C).

counter([],X, Cnt, [[X,Cnt]]).
counter([Y|L], X, Cnt, [[X,XCnt]|C]):-
  if_(X=Y,
    (
      Cnt1 #= Cnt+1,
      Cnt1 #=< XCnt,
      counter(L, X, Cnt1, [[X,XCnt]|C])
    ),
    (
      XCnt=Cnt,
      counter(L, Y, 1, C)
    )
  ).
于 2017-05-12T16:39:14.637 回答
3

cut 算子!通过修剪所有选择点来提交当前的推导路径。鉴于一些事实

fact(a).
fact(b).

您可以比较有无剪切的答案:

?- fact(X).
X = a ;
X = b.

?- fact(X), !.
X = a.

如您所见,一般查询现在只报告它的第一次成功。仍然,查询

?- fact(b), !.
true.

成功。这意味着,该 cut 违反了,as 逻辑合取的解释:

?- X = b, fact(X), !.
X = b.

?- fact(X), !, X=b.
false.

但根据我们对合取的理解,当 B ∧ A 成立时,A ∧ B 应该成立。那么为什么要这样做呢?

  • 效率:可以使用削减,这样它们只改变执行属性而不改变谓词的答案。这些所谓的绿色切割例如在 Richard O'Keefe 的Prolog 工艺中有所描述。如上所示,使用 cut 维护谓词的正确性比没有使用 cut 的情况要困难得多,但显然,正确性应该先于效率。

    看起来你的问题是绿色的,但我不能 100% 确定答案是否没有变化。

  • 否定:根据封闭世界假设的逻辑否定用cut表示。您可以将 neg(X) 定义为:

    neg(X) :-
      call(X),
      !,
      false.
    neg(_) :-
      true.
    

    因此,如果call(X)成功,我们将删除第二条规则的选择点并推导出 false。否则,什么都没有被削减,我们推导出真。请注意,这不是经典逻辑中的否定,它会受到切割的非逻辑效应的影响。假设您将谓词定义land/1为大陆之一:

    land(africa).
    land(america).
    land(antarctica).
    land(asia).
    land(australia).
    land(europe).
    

    然后将水定义为不在陆地上的一切:

    water(X) :-
      neg(land(X)).
    

    那么您可以正确获得:

    ?- water(pacific).
    true.
    
    ?- water(africa).
    false.
    

    但你也可以推导出:

    ?- water(space).
    true.
    

    这不应该成立。特别是,在经典逻辑中:

    land(africa) ∧
    land(america) ∧
    land(antarctica) ∧
    land(asia) ∧
    land(australia) ∧
    land(europe) → ¬ land(space).
    

    无效。同样,如果您在 Prolog 中使用否定,您应该清楚自己在做什么。

于 2017-05-09T09:54:59.873 回答
3

这是我使用的尝试if_/3

counter([], []).
counter([H|T], [[H,C]|OutT] ):-
         if_( 
               T=[], 
               (C = 1,OutT=[]), 
               (
                 [H|T] = [H,H1|T2],
                 if_(
                       H=H1, 
                       (counter([H1|T2], [[H1,C1]|OutT]), C is C1+1),
                       (C = 1, counter([H1|T2], OutT))
                     )
               )  
            ).  
于 2017-05-12T19:43:55.517 回答