У меня есть эти 3 предиката:
times(X, Y):-
Result is X * Y.
minus(X, Y):-
Result is X - Y.
plus(X, Y):-
Result is X + Y.
и я хочу передать например times(2,2)
в plus(X, Y)
вот так plus(times(2,2), minus(X, Y))
.
У меня есть эти 3 предиката:
times(X, Y):-
Result is X * Y.
minus(X, Y):-
Result is X - Y.
plus(X, Y):-
Result is X + Y.
и я хочу передать например times(2,2)
в plus(X, Y)
вот так plus(times(2,2), minus(X, Y))
.
Связь между заголовком вашего вопроса и текстом вашего вопроса мне неясна, и я думаю, что @false, вероятно, прав в том, что здесь есть более фундаментальное непонимание Пролога. Я не знаю, действительно ли это соответствует вашим потребностям или нет, но альтернативой здесь является написание собственного оценщика.
eval(times(X,Y), Result) :-
eval(X, XResult),
eval(Y, YResult),
Result is XResult * YResult.
eval(minus(X,Y), Result) :-
eval(X, XResult),
eval(Y, YResult),
Result is XResult - YResult.
eval(plus(X,Y), Result) :-
eval(X, XResult),
eval(Y, YResult),
Result is XResult + YResult.
Рекурсивные вызовы eval/2
внутри тела каждого из этих правил необходимы для обработки таких случаев, как plus(times(2,2), minus(X, Y))
. Тогда вам нужно правило для чисел:
eval(Num, Num) :- number(Num).
Это отлично работает для таких случаев:
?- eval(plus(times(2,2), minus(7,1)), Result).
Result = 10.
Это не поможет вам в таких случаях:
?- eval(plus(times(2,2), minus(X,Y)), Result).
ERROR: Out of local stack
Конечно, это сработает, если мы установим привязки для X и Y до того, как доберемся туда, но если вы хотите, чтобы он сгенерировал возможные решения для X и Y, вам не повезло, вам нужно использовать clpfd
. Причина этой любопытной ошибки, если вы проследите, заключается в том, что number(X)
, когда X
несвязано, является ложным, поэтому он фактически генерирует новые предложения, включающие структуры времени, минуса и плюса, и пробует их, а это не то, что вы хотите в оценщик.
Изменить: реализация printterm/1.
Предикат eval/2
показывает, как выполнить рекурсивный обход дерева. Принцип тот же, что и при создании красивого принтера. Я очень ленивый, поэтому я только набросаю его, вам придется заполнить детали самостоятельно.
printterm(T) :- format_term(T, Formatted), write(Formatted), nl.
format_term(plus(X,Y), Formatted) :-
format_term(X, XFormatted),
format_term(Y, YFormatted),
format(atom(Formatted), '(~a + ~a)', [XFormatted, YFormatted]).
% other format_term clauses here for other arithmetic expressions
format_term(X, X) :- number(X).
Надеюсь это поможет!
Во-первых, вам нужно понять, что на самом деле описывают предикаты Пролога: это не функции, а скорее отношения между значениями. Итак, если вы хотите иметь предикат для добавления, это должен быть предикат с тремя аргументами: plus(A, B, Sum)
. Таким образом, в Прологе результаты не появляются бесплатно, как во многих других языках.
Вместо
plus(X, Y):-
Result is X + Y.
вам нужно написать
plus(X, Y, Result) :-
Result is X + Y.
Далее вам нужно передать промежуточные значения. В языках, поддерживающих функции, это очень простая задача. Но в Прологе функциональные символы не интерпретируются. Так что либо вы кодируете все в отношениях, либо реализуете собственную версию (is)/2
. Для новичка лучше придерживаться первого варианта. Таким образом, вместо
..., plus(times(2,2), minus(X, Y)) ...
теперь пиши
..., times(2, 2, R), plus(R, minus(X, Y), S), ...
с S
окончательным результатом.
Обратите внимание, что если вы кодируете выражения непосредственно в отношениях, вы должны ввести промежуточные переменные, такие как R
выше.
Итак, ясно, что (прямая) реляционная запись менее элегантна для такой цели. Для очень точных областей Пролог также предлагает выражения, в частности (is)/2
, а затем в гораздо более общем виде library(clpfd)
.
Тем не менее, если вы новичок, рекомендуется сначала привыкнуть к реляционной нотации. И, возможно, было бы еще лучше сначала изучить арифметику преемника. До использования library(clpfd)
и до использования (is)/2
.
Альтернативой решению Даниэля является просмотр таких терминов, как plus(times(2,2), minus(5,3))
, как выражения объекты, поддерживающие набор операций, таких как "вычислять" или "дифференцировать". Это позволяет вам записывать такие цели, как:
| ?- plus(times(2,2), minus(5,3))::evaluate(Result).
Result = 6
yes
Преимущество в том, что правила для всех операций, которые применяются к определенному выражению, например. times/2
аккуратно инкапсулированы в соответствующий объект. т.е. вместо кластеризации предикатов по операции они группируются по (элементарному) выражению. Пример в этом духе, но в контексте получения и упрощения выражений можно найти здесь.
symdiff
круто! ?- (x**2+2*x+1)::diff(D), D::simplify(Sim).
-> D = 2*x**1*1+2*1, Sim = 2*x+2.
Возможность объявлять _ * _
как объект в Logtalk просто потрясающая!
- person Daniel Lyons; 22.10.2014
call
для вызова существующего предиката, например, переменной, содержащей этот предикат, и передать ее в список аргументов. Очень полезно для создания дженериков Prolog! Вы можете использоватьcall(Operator, ListOfArguments)
илиcall(Operator, Arg1, Arg2)
для создания функций, которые делают что-то, возвращают результаты, но вы не знаете и не хотите знать, что именно происходит, если результат имеет определенный тип, например+(1,Output),call(Arithmetic(Input1,Input2,Output)
. Выполните некоторую функцию /3, которая возвращает число, добавьте 1 к результату. - person G_V   schedule 30.01.2018