Вот одна попытка. Основная идея c состоит в том, что X
- это переменная, если она может быть объединена с как a
, так и b
. Но, конечно, мы не можем записать это как X = a, X = b
. Итак, нам нужен «унифицируемый» тест, который будет успешным без привязки переменных, таких как =/2
.
Во-первых, нам нужно определить отрицание самим, поскольку оно нечисто:
my_not(Goal) :-
call(Goal),
!,
false.
my_not(_Goal).
Это всего лишь приемлемо, если ваше понятие чистого Пролога включает call/1
. Предположим, что это так: -)
Теперь мы можем проверить унифицируемость, используя =/2
и шаблон «not not», чтобы сохранить успех при отмене привязок:
unifiable(X, Y) :-
my_not(my_not(X = Y)).
Теперь мы есть инструменты для определения var
/ nonvar
проверок:
my_var(X) :-
unifiable(X, a),
unifiable(X, b).
my_nonvar(X) :-
not(my_var(X)).
Давайте проверим это:
?- my_var(X).
true.
?- my_var(1).
false.
?- my_var(a).
false.
?- my_var(f(X)).
false.
?- my_nonvar(X).
false.
?- my_nonvar(1).
true.
?- my_nonvar(a).
true.
?- my_nonvar(f(X)).
true.
Остальное - ваше определение:
p(X) :-
my_nonvar(X),
!,
member(X, [2, 3]).
p(X) :-
member(X, [1, 2, 3]).
Что дает:
?- p(X).
X = 1 ;
X = 2 ;
X = 3.
?- p(1).
false.
Изменить: Использование call/1
не обязательно, и интересно написать решение без него:
not_unifiable(X, Y) :-
X = Y,
!,
false.
not_unifiable(_X, _Y).
unifiable(X, Y) :-
not_unifiable(X, Y),
!,
false.
unifiable(_X, _Y).
Посмотрите на эти вторые предложения каждого из этих предикатов. Они одинаковые! При декларативном прочтении этих пунктов любые два термина не могут быть унифицированы , но также любые два термина не могут быть унифицированы ! Конечно, вы не можете декларативно читать эти статьи из-за сокращения. Но я нахожу это особенно поразительным как иллюстрацию того, насколько катастрофически загрязнен разрез.