Имам тези 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 вероятно е прав, че тук има по-фундаментално недоразумение относно Prolog. Не знам дали това наистина отговаря на вашите нужди или не, но алтернативата тук е да напишете свой собствен оценител.
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).
Надявам се това да помогне!
Първо, трябва да разберете какво всъщност описват предикатите на Prolog: Те не са функции, а по-скоро отношения между стойности. Така че, ако искате да имате предикат за добавяне, това трябва да е предикат с три аргумента: plus(A, B, Sum)
. Така в Prolog резултатите не се показват безплатно, както в много други езици.
Вместо
plus(X, Y):-
Result is X + Y.
трябва да пишеш
plus(X, Y, Result) :-
Result is X + Y.
Това, което искате след това, е да предадете междинните стойности по-нататък. В езиците, които поддържат функции, това е много лесна задача. Но в Prolog функционалните символи не се интерпретират. Така че или кодирате всичко в отношения, или по друг начин прилагате своя собствена версия на (is)/2
. За начинаещи по-скоро се придържайте към първия вариант. По този начин, вместо
..., plus(times(2,2), minus(X, Y)) ...
сега пиши
..., times(2, 2, R), plus(R, minus(X, Y), S), ...
като S
е крайният резултат.
Обърнете внимание, че ако кодирате изрази директно в отношения, трябва да въведете междинни променливи като R
по-горе.
Толкова ясно, че (директната) релационна нотация е по-малко елегантна за цел като тази. За много прецизни области Prolog предлага и изрази, по-специално (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