Во-первых, вы не передаете функции, вы передаете выражения, и в результате получается то, что они оценивают.Поскольку это пролог, и мы можем делать такие вещи, я предпочитаю передавать в одном аргументе что-то вроде x=2
, а не два аргумента x, 2
, потому что я считаю его более читабельным.Так что предикат, который я ищу, будет выглядеть как evaluate(+Expression, +Var=Value, ?Result)
.
То, что мы должны здесь делать, - это индукция формы выражений.Таким образом, базовыми вариантами для выражений являются переменные и числа, поэтому давайте сначала разберемся с ними:
evaluate(Num, _, Num) :- number(Num).
evaluate(Var, Var=Value, Value).
Первый из них должен быть очевиден: если я оцениваю 3, я должен вернуться к 3, а я нетдействительно волнует, как выглядит привязка переменной.
Вторая, вероятно, выглядит тавтологически, но идея в том, что если я ищу значение переменной x
, и у меня есть привязка, которая выглядит какx=3
, тогда результат будет 3
.
Теперь вам нужно немного скопировать и вставить.Приятно то, что вы можете легко создавать здесь функции, которые сам Prolog не имеет встроенных.Дело в том, что это будет немного повторяться.
evaluate(A*B, Binding, Value) :-
evaluate(A, Binding, AValue),
evaluate(B, Binding, BValue),
Value is AValue * BValue.
evaluate(A+B, Binding, Value) :-
evaluate(A, Binding, AValue),
evaluate(B, Binding, BValue),
Value is AValue + BValue.
Итак, идея здесь в том, чтобы recur вниз по левой и правой ветвям об операторе, а затем применить этот оператор кэти две стороны.Этот маленький игрушечный оценщик будет работать для очень простых случаев, таких как:
?- evaluate(2*(5+x*x), x=2, E).
E = 18
Теперь у вас есть небольшая сложность, которая может оказаться довольно раздражающей, чтобы правильно обращаться с ней, вот чтоздесь вы хотите выполнить частичное вычисление выражения, когда у вас есть несколько переменных.Вот противный пример:
?- evaluate(x*(3+y*y), y=3, E).
false.
Вы действительно хотите, чтобы это вернуло x*12
.Теперь есть две проблемы с оценщиком игрушек, которые мешают этому.Во-первых, он не знает, что делать, если вы ищете переменную, и у нас нет привязки к ней, которую мы можем решить, изменив базовый вариант:
evaluate(Var, X=Value, Result) :-
atom(Var),
( Var = X ->
Result = Value
;
Result = Var
)
Это сразу же приведет к некоторым ошибкам:
ERROR: Arithmetic: `x/0' is not a function
ERROR: In:
ERROR: [9] _3344 is x*3
ERROR: [7] <user>
ERROR:
ERROR: Note: some frames are missing due to last-call optimization.
ERROR: Re-run your program in debug mode (:- debug.) to get more detail.
Проблема в том, что is/2
требует, чтобы выражение в правой части не содержало ничего, кроме чисел и операторов;в результате оно никогда не даст вам дерева.Мы можем попытаться решить эту проблему следующим образом:
evaluate(A*B, Binding, Value) :-
evaluate(A, Binding, AValue),
evaluate(B, Binding, BValue),
(number(AValue), number(BValue) ->
Value is AValue * BValue
;
Value = AValue * BValue
).
evaluate(A+B, Binding, Value) :-
evaluate(A, Binding, AValue),
evaluate(B, Binding, BValue),
(number(AValue), number(BValue) ->
Value is AValue + BValue
;
Value = AValue + BValue
).
На первый взгляд это работает:
?- evaluate(x*(3+y*y), y=3, E).
E = x*12 ;
false.
Однако вы сможете найти множество случаев, когда алгебраические манипуляции непроисходит, что объединит больше констант:
?- evaluate(3*3*x*3*3, y=3, E).
E = 9*x*3*3 ;
Левые постоянные факторы не будут складываться с правильными постоянными коэффициентами, потому что у нас нет никакого кода перестановки дерева здесь.И правильные постоянные факторы не суммируются по причинам, которые более четко проиллюстрированы с использованием write_canonical/1
:
?- write_canonical(3*3*x*3*3).
*(*(*(*(3,3),x),3),3)
Как вы, вероятно, можете догадаться из этого, самые внутренние константы складываются вместе, образуя 9 * x,который мы затем пытаемся умножить на три.Поскольку 9*x
не является числом, оно не будет складываться вместе с *3
.И теперь, когда у вас есть фрагмент дерева с левой стороны, он не будет складываться вместе со следующими *3
.
Обращаться ко всем здесь специальным образом будет неприятно, но вы могли бысделай это, если нужно.Если честно, я понятия не имею, как можно было бы построить достаточно полную систему алгебры, которая могла бы справиться с этими вещами.Я подозреваю, что вам нужна более мощная структура данных, чем дерево разбора, но кроме этого я ничего не знаю.Во всяком случае, я надеюсь, что это помогает сейчас.