findall/3 错误地评估为 false

findall/3 incorrectly evaluating to false

我正在创建一个应该允许通过图形进行搜索的程序,但是当对 findall/3 的调用计算结果为 false 时,应该 return 后继节点列表的函数失败了。当我在 find_successors 函数之外单独尝试 findall 函数时,它运行良好,但由于某种原因,在 find_successors 函数内部它只是读取 false。通过图形调试器,我什至可以看到它找到了所有的解决方案。这是代码:

find_successors(Start, Out) :- 
    entity(Start),
    (findall(X, is_a(Start, X), O), append([], O, OL1); OL1 = []),
    (findall(X, is_a(X, Start), O), OL2 = O; OL2 = []),

    (findall(X, has(Start, X), O), append([], O, OL3); OL3 = []),
    (findall(X, has(X, Start), O), append([], O, OL4); OL4 = []),

    (findall(X, able_to(Start, X), O), append([], O, OL5); OL5 =[]),
    (findall(X, able_to(X, Start), O), append([], O, OL6); OL6 = []),

    (findall(X, used_to(Start, X), O), append([], O, OL7); OL7 = []),
    (findall(X, used_to(X, Start), O), append([], O, OL8); OL8 = []),

    append([OL1, OL2, OL3, OL4, OL5, OL6, OL7, OL8], Out).

entity(wings).
entity(fly).
entity(bird).
entity(legs).
entity(feathers).
entity('body covering').
entity(animal).
entity(dog).
entity(fur).
entity(aves).
entity(reptile).
entity(snake).
entity(scales).

f_is_a(bird, aves).
f_is_a(bird, animal).
f_is_a(snake, reptile).
f_is_a(snake, animal).
f_is_a(dog, mammal).
f_is_a(dog, animal).
f_is_a(feathers, 'body covering').
f_is_a(fur, 'body covering').
f_is_a(mammal, animal).
f_is_a(reptile, animal).
f_is_a(aves, animal).
is_a(X, H) :- !, f_is_a(X, H).
is_a(X, H) :- !, \+f_is_a(X, P), H = X.
is_a(X, H) :- !, is_a(X, P), is_a(P, H).

f_has(bird, wings).
f_has(bird, feathers).
f_has(bird, legs).
f_has(aves, wings).
f_has(aves, feathers).
f_has(aves, legs).
f_has(dog, legs).
f_has(dog, fur).
f_has(mammal, legs).
f_has(mammal, fur).
f_has(snake, scales).
f_has(reptile, scales).
has(X, H) :- !, f_has(X, H).
has(X, H) :- !, \+f_has(X, P), H = X.
has(X, H) :- !, has(X, P), has(P, H).

used_to(wings, fly).
used_to(legs, walk).

able_to(bird, fly).
able_to(bird, walk).
able_to(dog, walk).
able_to(X, Y) :- used_to(X1, Y), has(X, X1).

您不断尝试重复使用同一个变量,但是一旦变量被绑定,您就不能再使用它。所以所有这些:

        here              here
         |                  |
         v                  v
(findall(X, is_a(Start, X), O), append([], O, OL1); OL1 = []),
(findall(X, is_a(X, Start), O), OL2 = O; OL2 = []),

(findall(X, has(Start, X), O), append([], O, OL3); OL3 = []),
(findall(X, has(X, Start), O), append([], O, OL4); OL4 = []),

(findall(X, able_to(Start, X), O), append([], O, OL5); OL5 =[]),
(findall(X, able_to(X, Start), O), append([], O, OL6); OL6 = []),

(findall(X, used_to(Start, X), O), append([], O, OL7); OL7 = []),
(findall(X, used_to(X, Start), O), append([], O, OL8); OL8 = []),

而且这些台词中的每一行都非常非常奇怪。我需要分解它才能真正弄清楚发生了什么。仅采取其中一项:

(   findall(X, used_to(Start, X), O),
    append([], O, OL7)
;   OL7 = []
)

(顺便说一句,这是你应该尝试编写析取的方式,否则它们很容易被误读)

append([], A, B) 等同于 A = B.

然后,findall/3总是成功,即使没有解;它只是给你一个空列表!

?- findall(X, between(2, 1, X), Xs).
Xs = [].

所以整个事情是完全没有必要的,除了对 findall/3.

的调用,你也可以扔掉所有东西

旁注:您使用的析取与您认为的不同。这是一个小例子:

?- ( A = 1 ; A = 2 ).

你认为会发生什么?

你应该建议我们打电话给 find_successors(Start, Out) 并说出期望值。

如果没有它,很难说出您的代码哪里错了,但是...某些点没有特别的顺序...

(1) append/3 concatenate 将第三个参数与从第一个和第二个列表中的元素连接起来获得的列表统一;所以

append([], O, OL1)

第一个参数中没有元素,统一 OOL1 所以没有用;你可以把所有的行都写成

(findall(X, is_a(Start, X), O), append([], O, OL1); OL1 = []),

作为

(findall(X, is_a(Start, X), OL1) ; OL1 = []),

(2) findall/3 return 当将第三个参数与空列表统一时(当找不到值时)也是如此,所以我不明白你为什么写

(findall(X, is_a(Start, X), OL1) ; OL1 = []),

当第二部分 (OL1 = []) 永远不会执行时(如果我没记错的话)当 OL1[] 统一时 findall/3 什么也没找到;我想你可以简单地写

findall(X, is_a(Start, X), OL1),

(3) 我只知道有三个参数的 append;所以我不明白

的意思
append([OL1, OL2, OL3, OL4, OL5, OL6, OL7, OL8], Out)

您的意图是写

append([], [OL1, OL2, OL3, OL4, OL5, OL6, OL7, OL8], Out)

?

在这种情况下,考虑到计数 (1) 和 (2),您可以将 find_successors/2 简单地写为

find_successors(Start, [OL1, OL2, OL3, OL4, OL5, OL6, OL7, OL8]) :- 
    entity(Start),
    findall(X, is_a(Start, X), OL1),
    findall(X, is_a(X, Start), OL2),
    findall(X, has(Start, X), OL3),
    findall(X, has(X, Start), OL4),
    findall(X, able_to(Start, X), OL5),
    findall(X, able_to(X, Start), OL6),
    findall(X, used_to(Start, X), OL7),
    findall(X, used_to(X, Start), OL8).

(4) 我不喜欢削减 (!) 所以也许我错了但是......你为什么把 ! 作为 is_a/2 中的第一个元素?

is_a(X, H) :- !, f_is_a(X, H).
is_a(X, H) :- !, \+f_is_a(X, P), H = X.
is_a(X, H) :- !, is_a(X, P), is_a(P, H).

如果我没记错,第一个子句 (!, f_is_a(X, H)) 中的切入会禁用第二个和第三个子句,因此,如果 f_is_a(X, H) 失败,则永远不会验证第二个和第三个子句.

你确定你的意图不是

is_a(X, H) :- f_is_a(X, H), !.
is_a(X, H) :- \+f_is_a(X, P), H = X, !.
is_a(X, H) :- is_a(X, P), is_a(P, H), !.

或更好

is_a(X, H) :- f_is_a(X, H), !.
is_a(X, X) :- \+f_is_a(X, _), !.
is_a(X, H) :- is_a(X, P), is_a(P, H), !.

?

还是根本不剪?

(5) 与has/3相同的切割问题;我怀疑

has(X, H) :- !, f_has(X, H).
has(X, H) :- !, \+f_has(X, P), H = X.
has(X, H) :- !, has(X, P), has(P, H).

错了,你的意图是

has(X, H) :- f_has(X, H), !.
has(X, H) :- \+f_has(X, P), H = X, !.
has(X, H) :- has(X, P), has(P, H), !.

或更好

has(X, H) :- f_has(X, H), !.
has(X, X) :- \+f_has(X, _), !.
has(X, H) :- has(X, P), has(P, H), !.

还是根本不剪?