Как объединить два основных термина в прологе - PullRequest
1 голос
/ 23 апреля 2019

Я пишу предикат в прологе: вычислить (функция, A, Value, Expr) Функция является функцией, может иметь переменную А или нет Значение - это значение A, а Expr - это выражение функции.

Например:

calculate(2*3,x,2,E) - возврат E = 6.

calculate(x+2,x,2,E) - возврат E = 4.

calculate(2*(5+x*x),x,2,E) - возврат E = 18.

calculate(x*(3+y*y),y,3,E) - возврат E = x * 12.

calculate(Function,A,Value,Expr):-
    A is Value, 
    %obviously, it is wrong, my question is how to unify these two guys.
    atom(A),
    num(Value),
    Expr is Function.

Приведенный выше код может привести к примеру 1, который не имеет переменной в функции, но он может достичь, когда функция имеет переменную.

Ответы [ 2 ]

1 голос
/ 23 апреля 2019

Это требует больше контекста.Глядя на это, кажется, что real цель состоит в том, чтобы проанализировать "функции" (арифметические выражения с переменными?) И упростить их.

Пролог дает вам большую часть анализа бесплатно, вощущение, что выражение типа 2*x + 3 также является допустимым термином Пролога, и у него даже есть дерево разбора, которое вы ожидаете:

$ swipl
Welcome to SWI-Prolog (threaded, 64 bits, version 8.1.3-31-g32d2d0a1d)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
Please run ?- license. for legal details.

For online help and background, visit http://www.swi-prolog.org
For built-in help, use ?- help(Topic). or ?- apropos(Word).

?- T = 2*x + 3*y, display(T).
+(*(2,x),*(3,y))
T = 2*x+3*y.

Так что теперь вам нужно выяснить две вещи:

  1. Как точно определяется ваш язык: каковы операторы, приоритет, что такое значение и что такое переменная и т. Д.
  2. Как вы упрощаете?

Второй пункт может дать вам больше всего работы.Проблема, над которой вы сейчас застряли, относительно проста, но вам нужен другой подход.Вам просто нужно пройти по дереву и заменить все вхождения "переменной" (что у вас есть в вашем втором аргументе) значением (что вы имеете в своем третьем аргументе).Тогда вам придется все же упростить.

Чтобы увидеть, как заменить, вы можете попробовать прочитать «Искусство Пролога», Глава 3, «Рекурсивное программирование».На страницах 70-80 есть даже примеры программ, которые вы можете использовать почти как они есть.Есть также полностью проработанный пример программы, которая делает производные.Это не то, что вам нужно, но подход очень близок к тому, что вам, вероятно, нужно.

И слово предупреждения: когда вы говорите «функция» или «переменная» в контексте Пролога, они могут означать разныевещи для людей в мышлении Пролог, так что вы будете говорить друг с другом некоторое время.Если вы можете задать свой вопрос на более формальном языке, вам, возможно, будет легче с ним справиться.

1 голос
/ 23 апреля 2019

Во-первых, вы не передаете функции, вы передаете выражения, и в результате получается то, что они оценивают.Поскольку это пролог, и мы можем делать такие вещи, я предпочитаю передавать в одном аргументе что-то вроде 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.

Обращаться ко всем здесь специальным образом будет неприятно, но вы могли бысделай это, если нужно.Если честно, я понятия не имею, как можно было бы построить достаточно полную систему алгебры, которая могла бы справиться с этими вещами.Я подозреваю, что вам нужна более мощная структура данных, чем дерево разбора, но кроме этого я ничего не знаю.Во всяком случае, я надеюсь, что это помогает сейчас.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...