Findall/3 неправильно оценивается как ложный

Я создаю программу, которая должна разрешать поиск по графу, но функция, которая должна возвращать список узлов-преемников, терпит неудачу, когда вызов findall/3 оценивается как false. Когда я пытаюсь найти функцию findall отдельно от функции find_successors, она работает отлично, но по какой-то причине внутри функции 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).

2 ответа

Решение

Вы продолжаете пытаться повторно использовать одну и ту же переменную, но как только переменная связана, вы не можете использовать ее снова. Итак, все это:

        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 объединить третий аргумент со списком, полученным путем объединения элементов из первого и второго списка; так

append([], O, OL1)

с первым аргументом без элементов, объединить O с OL1 так бесполезно; Вы можете написать все строки в форме

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

как

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

(2) findall/3 верните true также, когда объединяете третий аргумент с пустым списком (когда не находит значения), поэтому я не понимаю, почему вы пишете

(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), !.

Или вообще не резать?

Другие вопросы по тегам