Пролог добавление на завернутые значения - PullRequest
0 голосов
/ 24 сентября 2018

Я написал тестовую программу с привязками (фактами) между атомами и числами.

bind(a, 3).
bind(b, 4).
bind(c, 5).

Как часть игрушечного интерпретатора, я хочу иметь возможность выполнять сложения на этих атомах, используя собственные арифметические операторы Пролога.,Например, я хочу иметь возможность выполнить этот запрос:

% val(X) is the value bound to X
?- X is val(a) + val(b).
X = 7.

Однако я изо всех сил пытаюсь найти способ разрешить это добавление.Мой первый подход был бы таким:

% val(X, Y): Y is the value bound to X
val(X, Y) :- bind(X, Y).

% Make val an arithmetic function
:- arithmetic_function(val/1).

Однако arithmetic_function/1 больше не является частью Пролога (или, по крайней мере, SWI-Пролог говорит, что он устарел), поэтому я не могу его использовать.Тогда я полагал, что лучшим решением было бы перегрузить оператор +, чтобы учесть это:

% val(X, Y): Y is the value bound to X
val(val(X), Y) :- bind(X, Y).

% Overload the + operator
+(val(_X, XVal), val(_Y, YVal)) :- XVal + YVal.

Но здесь у меня весь синтаксис испорчен, потому что я действительно не знаю, какперегрузить родную арифметическую операцию.Когда я набираю образец запроса, сделанного ранее, SWI-Prolog говорит: ERROR: Arithmetic: ``val(a)' is not a function.

Не могли бы вы получить подсказки о возможном решении, о лучшем подходе или о том, что я пропустил?

Ответы [ 3 ]

0 голосов
/ 24 сентября 2018

Из документов я решил, что вы должны использовать function_expansion / 3.Но я не могу заставить его работать, вместо этого goal_expansion может, но не очень привлекательно ... например, если вы сохраните следующие определения в файле bind.pl (просто дляскажем)

:- module(bind, [test/0]).

:- dynamic bind/2.

bind(a, 3).
bind(b, 4).
bind(c, 5).

% :- multifile user:goal_expansion/2.
user:goal_expansion(val(X), Y) :- bind(X, Y).
user:goal_expansion(X is Y, X is Z) :- expand_goal(Y, Z).
user:goal_expansion(X + Y, U + V) :- expand_goal(X, U), expand_goal(Y, V).

test :-
     X is val(a) + val(b), writeln(X).

и обратитесь к нему, вы можете запустить свой тест:

?- test.
7

изменить

после предложения Пауло, вотрасширенное решение, которое должно работать для каждого двоичного выражения.

user:goal_expansion(X is Y, X is Z) :- expr_bind(Y, Z).

expr_bind(val(A), V) :- !, bind(A, V).
expr_bind(X, Y) :-
     X =.. [F, L, R],  % get operator F and Left,Right expressions
     expr_bind(L, S),  % bind Left expression
     expr_bind(R, T),  % bind Right expression
     Y =.. [F, S, T].  % pack bound expressions back with same operator
expr_bind(X, X).       % oops, I forgot... this clause allows numbers and variables

, определив user в качестве целевого модуля для goal_expansion, работает на CLI:

?- R is val(a)*val(b)-val(c).
R = 7.

edit

Теперь давайте обобщим некоторые другие арифметические операторы, используя тот же скелет, который expr_bind использует для двоичных выражений:

user:goal_expansion(X, Y) :-
     X =.. [F,L,R], memberchk(F, [is, =<, <, =:=, >, >=]),
     expr_bind(L, S),
     expr_bind(R, T),
     Y =.. [F, S, T].

и унарныйоператоры (я не могу вспомнить никого, кроме минуса, поэтому я показываю более простой способ, чем (= ..) / 2):

...
expr_bind(-X, -Y) :- expr_bind(X, Y).
expr_bind(X, X).

Теперь мы получаем

?- -val(a)*2 < val(b)-val(c).
true.
0 голосов
/ 24 сентября 2018

Один из способов сделать это - использовать параметрические объекты Logtalk (Logtalk работает на SWI-Prolog и 11 других системах Prolog; это делает это решение очень переносимым).Идея состоит в том, чтобы определить каждую арифметическую операцию как параметрический объект, который понимает сообщение eval/1.Сначала мы определяем протокол, который будет реализован объектами, представляющими арифметические операции:

:- protocol(eval).

    :- public(eval/1).

:- end_protocol.

Базовый параметрический объект понимает val/1 и содержит таблицу bind/2:

:- object(val(_X_), implements(eval)).

    eval(X) :-
         bind(_X_, X).

    bind(a, 3).
    bind(b, 4).
    bind(c, 5).

:- end_object.

Я приведу здесь только пример реализации арифметического сложения:

:- object(_X_ + _Y_, implements(eval)).

    eval(Result) :-
        _X_::eval(X), _Y_::eval(Y),
        Result is X + Y.

:- end_object.

Пример вызова (при условии, что вышеприведенные объекты сохранены в файле eval.lgt):

% swilgt
...
?- {eval}.
% [ /Users/pmoura/Desktop/eval.lgt loaded ]
% (0 warnings)
true.

?- (val(a) + val(b))::eval(R).
R = 7.

Это может бытьинтересное решение, если вы планируете реализовать больше функциональности, кроме оценки выражений.Например, подобное решение, но для символьного дифференцирования арифметических выражений, можно найти по адресу:

https://github.com/LogtalkDotOrg/logtalk3/tree/master/examples/symdiff

Это решение также будет работать в случае сгенерированных во время выполнения выражений (обычно решения на основе расширения терминовработать только во время компиляции исходного файла и на верхнем уровне.)

Если вас интересует только оценка выражений, решение Capelli более компактно и сохраняет is/2 для оценки.При необходимости его также можно сделать более переносимым, используя переносимый механизм расширения терминов Logtalk (но обратите внимание на предостережение в предыдущем абзаце).

0 голосов
/ 24 сентября 2018

Возможно, это не совсем то, что я искал, но у меня была идея:

compute(val(X) + val(Y), Out) :-
  bind(X, XVal),
  bind(Y, YVal),
  Out is XVal + YVal.

Теперь я могу выполнить следующий запрос:

?- compute(val(a) + val(c), Out).
Out = 8.

Теперь мне нужноопределите compute для каждой интересующей меня арифметической операции, затем попросите мой интерпретатор выполнить через нее выражения.

...