Как я могу передать предикат в качестве параметра для другого предиката в Прологе?

У меня есть эти 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)).


person HowTheF    schedule 19.10.2014    source источник
comment
прочтите о метавызовах   -  person CapelliC    schedule 19.10.2014
comment
Основываясь на том, что @CapelliC написал выше, в SWI-прологе вы можете использовать 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


Ответы (3)


Связь между заголовком вашего вопроса и текстом вашего вопроса мне неясна, и я думаю, что @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).

Надеюсь это поможет!

person Daniel Lyons    schedule 19.10.2014
comment
Спасибо, у вас есть идея для предиката printterm/1, который будет использоваться для такой команды ?- printterm( plus(minus(8,2), times(4,-3))). Распечатайте: ((8 - 2) + (4 * -3)) - person HowTheF; 23.10.2014

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

person false    schedule 19.10.2014

Альтернативой решению Даниэля является просмотр таких терминов, как plus(times(2,2), minus(5,3)), как выражения объекты, поддерживающие набор операций, таких как "вычислять" или "дифференцировать". Это позволяет вам записывать такие цели, как:

| ?- plus(times(2,2), minus(5,3))::evaluate(Result).
Result = 6
yes

Преимущество в том, что правила для всех операций, которые применяются к определенному выражению, например. times/2 аккуратно инкапсулированы в соответствующий объект. т.е. вместо кластеризации предикатов по операции они группируются по (элементарному) выражению. Пример в этом духе, но в контексте получения и упрощения выражений можно найти здесь.

person Paulo Moura    schedule 20.10.2014
comment
Святое дерьмо 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
comment
Спасибо. Параметрические объекты очень универсальны. См., например. link.springer.com/chapter/10.1007%2F978-3- 642-20589-7_4 - person Paulo Moura; 22.10.2014