Вы столкнулись с интересной глобальной вещью о Prolog, которую операторы действительно выводят на поверхность, а именно то, что операторы - это просто еще один способ построения терминов.
Эти ваши определения операторов позволяют создавать терминыс другой стороны, например:
?- X = neg a or b.
X = neg a or b.
Наделение этих терминов значением , с другой стороны, потребует от вас создания другого предиката. Это связано с тем, что термины в Прологе не являются выражениями, которые сокращаются сами по себе - отчасти поэтому вам нужно использовать is/2
, чтобы уменьшить арифметическое выражение до значения. Даже чисто арифметические вещи - это просто термины в Прологе:
?- X = 16*3 + 4.
X = 16*3+4.
Это не какое-то особенное поведение =/2
в Прологе. Так устроены условия. Для уменьшения значения требуется развертывание другого предиката:
?- X is 16*3+4.
X = 52.
Итак, вы, похоже, сделали, что ваш оператор neg
вызвал предикат с двумя аргументами для его уменьшения и что ваш оператор or
имеетвызвал предикат из трех аргументов для его уменьшения. На самом деле ничего из этого не произошло, все, что делают ваши операторские объявления, это позволяет вам создавать термины, подобные приведенному выше, neg a or b
. Таким образом, вам все еще нужно создать отдельный предикат для их оценки, где ваша семантика входит в картину. Итак, давайте реализуем предикат eval/2
, который преобразует ваш термин в требуемое значение результата:
eval(neg X, [neg X|Result]) :-
eval(X, Result).
eval(X or Y, [X or Y|Result]) :-
eval(X, R1),
eval(Y, R2),
append(R1, R2, Result).
eval(X, [X]) :- atomic(X).
Ключевая идея здесь состоит в том, чтобы соответствовать тому, что дают ваши операторы, и рекурсивно снимать по одному слою за раззову остальных. Наш базовый случай - это «атомные значения», то есть атомы типа a
.
. Это дает нам то, что вы ищете:
?- eval(neg (a or b), R).
R = [neg (a or b), a or b, a, b]
Обратите внимание, что is/2
являетсяоператор. Вы также можете определить оператор, который «работает», объявив оператора, а затем предоставив правила для его приложения. Это не поможет вам в этом случае, потому что ваши примеры для neg
и or
требуют, чтобы вы сохранили структуру, а не отбросили ее. is/2
, с другой стороны, предполагает, что структура существует в правом аргументе, и уменьшает ее до значения для левого. Вы можете сделать что-то подобное, например, сделав eval/2
оператором, и в этом случае оператор будет использоваться слева от :-
, например, так:
[Neg X|R] eval (neg X) :- R eval X.
Однако я нахожу этот видтрудно обрабатывать и определенно избегать этого, если это не приведет к большей ясности.
Кстати, вы, скорее всего, захотите заменить xfx
в своем вызове op/3
на xfy
или yfx
, поскольку конструкции типа a or b or c
не будут работать с xfx
из-за конфликта приоритетов оператора. С xfy
он будет проанализирован как a or (b or c)
, а с yfx
он будет проанализирован как (a or b) or c
, что, вероятно, более полезно. А также, если вы намереваетесь всегда заключать в скобки то, с чем вы используете neg
, тогда вам не нужно объявлять оператор для него - цель унарного оператора состоит в том, чтобы просто пропустить парены(и контролируйте, сколько из того, что следует, потребляется им.)
Надеюсь, это поможет!