Давайте немного упростим задачу, сначала рассмотрев is_member/2
.
Вы пишете это как:
is_member(X, [Head,Tail]):-
X == Head;
is_member(X, Tail).
Подумайте, как легко это неправильно истолковать как:
is_member(X, [Head,Tail]):-
X == Head,
is_member(X, Tail).
Упражнение: что я изменил?
По этой причине я рекомендую следующий макет:
is_member(X, [Head,Tail]):-
( X == Head
; is_member(X, Tail)
).
А теперь перейдем к нескольким тестовым случаям!
Во-первых, всегда рекомендуется публиковать самый общий запрос. Здесь просто спрашивается: Есть ли вообще какие-нибудь решения?
?- is_member(E, Ls).
nontermination
Это не хороший знак!
Итак, давайте попробуем несколько конкретных случаев. Например, a
член пустого списка?
?- is_member(a, []).
false.
Это хорошо! Это то, что мы ожидали.
Тогда является ли a
членом списка [a]
?
?- is_member(a, [a]).
false.
Это определенно неправильно!
Я рекомендую вам начать с этого, а затем перейти к более сложным определениям. Подойдите к этому систематически:
- Запишите, что следует удерживать.
- Попробуйте выполнить самый общий запрос, чтобы узнать, сможете ли вы получить ответы от своей программы.
- Попробуйте конкретные тестовые примеры.
- Подумайте, что на самом деле означает: предикат можно использовать в нескольких направлениях, и во многих из этих случаев то, как вы описали предикат, не имеет смысла. Например, и список, и элемент уже могут быть даны, и в этом случае нечего «добавлять» или «удалять».
Чтобы определить причины неожиданного сбоя в вашей программе, используйте нарезание программ, например, используя следующее определение для обобщения целей:
:- op(950,fy, *).
*_.
Теперь вы можете написать:
is_member(X, [Head,Tail]):-
( * X == Head
; * is_member(X, Tail)
).
что является массовым обобщением вашей исходной программы и фактически декларативно эквивалентно:
is_member(X, [Head,Tail]) :- true.
Тестовый пример выше по-прежнему не работает с этим фрагментом, который показывает, что программа по-прежнему слишком специфична:
?- is_member(a, [a]).
false.
person
mat
schedule
16.11.2016