【问题标题】:Prolog: why my predicate returns false?Prolog:为什么我的谓词返回false?
【发布时间】:2018-02-27 09:41:56
【问题描述】:

所以我写了一个谓词来计算一个元素在列表列表中出现的次数。

count([], _, 0).                                  #base case

count([[Elem|Rest]|OtherLists], Elem, Count) :-   #Elem is the head of sublist
    !,
    count([Rest|OtherLists], Elem, NewCount),
    succ(NewCount, Count).

count([[_|Rest]|OtherLists], Elem, Count) :-      #Elem is not the head of sublist
    count([Rest|OtherLists], Elem, Count).

count([[]|OtherLists], Elem, Count) :-            #Head sublist is an empty list
    count(OtherLists, Elem, Count).

现在,如果我使用以下内容查询谓词:

count([[1,2,3],[4,1,5],[4,6,1]], 1, X).

它返回 X = 3,这是正确的,但如果我继续查询,它也会说“假”。

所以它正确计算元素,但我不能在其他谓词中使用这个谓词,因为它最终返回 FALSE。

我做错了什么?

【问题讨论】:

  • 您需要陈述假设。子列表中的所有元素都是唯一的?所有子列表的长度都相同吗?所以你不能拥有[[1,2,1],[3,3,4],[2,1,3,3]]
  • 不,元素不一定是唯一的,子列表可以有不同的长度。
  • 继续查询时显示“false”,因为 Prolog 在找到 X = 3 后说“没有更多解决方案”。是的,使用更多削减将“解决”这个问题,但会牺牲解决方案的通用性。

标签: prolog


【解决方案1】:

当Prolog在寻找解决方案的过程中遇到一个“选择点”(代码中可以回来寻找更多可能解决方案的地方)时,它会显示解决方案并提示您寻找更多可能的解决方案。如果找不到更多,则显示“false”。这不是您的逻辑中的任何错误。这就是 Prolog 的工作方式。

删除选择点并不总是可取的。这取决于您对谓词的目标是什么。使用切割删除选择点的危险在于,选择点可能是通往有效替代解决方案的路径,而切割会阻止您的程序找到这些解决方案。

让我们在你的答案中使用新提议的削减来尝试你的更新程序:

| ?- count([[1,2,3],[4,1,5],[4,6,1]], 1, X).

X = 3

yes
| ?- count([[1,2,1,3],[4,1,5],[4,6,1]], 1, X).

X = 4

yes
| ?- count([[1,2,1,3],[4,1,5],[4,6,1],[1]], 1, X).

X = 5

到目前为止,一切都很好。这些看起来像是完整而正确的答案。我相信只要第一个参数完全绑定没有变量,您的额外剪辑(包括您的原始剪辑)将产生正确的答案。让我们尝试一个更有趣的查询:

2 ?- count([[A,2,B],[C,1,D]], 1, X).
A = B, B = C, C = D, D = 1,
X = 5.

3 ?-

谓词找到了一个解决方案。然而,不是还有更多吗?这个呢?

A = _ % something other than 1
B = C, C = D, D = 1,
X = 4.

这也是一个正确的解决方案,但是谓词找不到它。

另外,这个查询呢?

2 ?- count([[1,2,1,3],[4,1,5],[4,6,1],[1]], E, X).
E = 1,
X = 5.

3 ?-

同样,只找到一个解决方案。但不是还有更多吗? E = 4X = 2 呢?

如果我们从原始谓词中删除所有削减以尝试获得所有正确的解决方案,那么我们也会得到不正确的解决方案:

2 ?- count([[1,2],[3,1,4],[1]], 1,X).
X = 3 ;
X = 2 ;
X = 2 ;
X = 1 ;
X = 2 ;
X = 1 ;
X = 1 ;
X = 0 ;
false.
2 ?- count([[1,2,1,3],[4,1,5],[4,6,1],[1]], E, X).
E = 1,
X = 5 ;
E = 1,
X = 4 ;
E = 1,
X = 3 ;
...

因此,如果需要更多通用性,则需要构建更有效的解决方案。

count_occurrences_lol([], _, 0).
count_occurrences_lol([List|Lists], X, Count) :-
    count_occurrences(List, X, C1),        % Count occurrences in this list
    count_occurrences_lol(Lists, X, C2),   % Count occurrences in remaining sublists
    Count is C1 + C2.                       % Total the counts

count_occurrences([], _, 0).
count_occurrences([X|Xs], X, Count) :-
    count_occurrences(Xs, X, C1),
    Count is C1 + 1.
count_occurrences([X1|Xs], X, Count) :-
    dif(X1, X),
    count_occurrences(Xs, X, Count).

现在我们得到以下信息:

3 ?- count_occurrences_lol([[1,2],[3,1,4],[1]], 1,X).
X = 3 ;
false.

正如预期的那样,只有一种解决方案。以及以下内容:

5 ?- count_occurrences_lol([[A,2,B],[C,1,3]], 1, X).
A = B, B = C, C = 1,
X = 4 ;
A = B, B = 1,
X = 3,
dif(C, 1) ;
A = C, C = 1,
X = 3,
dif(B, 1) ;
A = 1,
X = 2,
dif(B, 1),
dif(C, 1) ;
B = C, C = 1,
X = 3,
dif(A, 1) ;
B = 1,
X = 2,
dif(A, 1),
dif(C, 1) ;
C = 1,
X = 2,
dif(A, 1),
dif(B, 1) ;
X = 1,
dif(A, 1),
dif(B, 1),
dif(C, 1) ;
false.

3 ?- count_occurrences_lol([[1,2,1,3],[4,1,5],[4,6,1],[1]], E, X).
E = 1,
X = 5 ;
E = 2,
X = 1 ;
E = 3,
X = 1 ;
E = 4,
X = 2 ;
E = 5,
X = 1 ;
E = 6,
X = 1 ;
X = 0,
dif(E, 1),
dif(E, 1),
dif(E, 6),
dif(E, 4),
dif(E, 5),
dif(E, 1),
dif(E, 4),
dif(E, 3),
dif(E, 1),
dif(E, 2),
dif(E, 1).

4 ?-

预期的几种可能的解决方案。

【讨论】:

  • 感谢您发布此内容,但是,我看不出这如何解决我的问题。还有比我的示例中更多的代码。如果您能详细说明您的答案,我将不胜感激。
  • @afonja 我很抱歉。我重写了我的答案。
  • 感谢您详细解释。在我的例子中,count/3 被用作另一个谓词的规则,以确保列表仅包含 N 个特定元素,并且不太可能使用多个变量进行查询。例如listCorrect(List) :- count(List, a, X), count(List, b, Y), X == 5, Y == 5.,但它总是返回 false,即使 X 和 Y 都是 5。还有其他方法吗?
  • @afonja 我明白你的意思。在这种情况下,您仍然有一个选择点,这会导致 Prolog 在XY 5 之后返回寻找更多解决方案。它确实首先通过检查成功,但在尝试更多之后给出“false”解决方案。这会导致某种问题吗?这是正常的 Prolog 行为。
  • @afonja 你可以写,listCorrect(List) :- count(List, a, X), count(List, b, Y), X == 5, Y == 5, !. 甚至listCorrect(List) :- count(List, a, 5), count(List, b, 5), !.(如果你希望两者都是 5,则不需要额外的变量)。但是我的回答中关于削减的内容仍然适用。
【解决方案2】:

好的,看起来它正在回溯“Elem 不是子列表的头部”的部分,我可以通过将其更改为来修复它:

count([[_|Rest]|OtherLists], Elem, Count) :-      #Elem is not the head of sublist
    !,
    count([Rest|OtherLists], Elem, Count).

如果有人可以确认这是否是正确的解决方案。谢谢

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多