Обратимые числовые вычисления в Прологе

При чтении SICP я столкнулся с главой 4.4 логического программирования. Затем я начал изучать язык программирования Пролога и попытался понять некоторые простые присвоения в Прологе. Я нашел, что Пролог, кажется, испытывает затруднения из-за числовых вычислений.

Вот вычисление факториала в стандартном Прологе:

f(0, 1).
f(A, B) :- A > 0, C is A-1, f(C, D), B is A*D.

Проблемы, которые я нахожу, - то, что я должен представить две вспомогательных переменные (C и D), новый синтаксис (is) и что проблема необратима (т.е. f(5,X) работы как ожидалось, но f(X,120) не делает).

Наивно, я ожидаю это по крайней мере C is A-1, f(C, D) выше может быть заменен f(A-1,D), но даже который не работает.

Мой вопрос: Почему я должен сделать этот дополнительный "материал" в числовых вычислениях, но не в других запросах?

Я действительно понимаю (и SICP вполне соглашается с этим), что в общей информации о "то, что сделать", недостаточно для ответа на вопрос, "как сделать это". Таким образом, декларативное знание в (по крайней мере немного) математические проблемы недостаточно для фактического решения этих проблем. Но это вызывает следующий вопрос: Как делает этот дополнительный "материал" в Прологе, помогают мне ограничить формулировку просто теми проблемами, где "то, что сделать", достаточно для ответа, "как сделать это"?

13
задан false 30 May 2014 в 23:11
поделиться

3 ответа

Забудьте о переменных и подумайте, что A и B - это просто название значения, которое можно поместить в этот раздел (X: - Y). , чтобы сделать его доступным. Подумайте о X = (2 + (3 * 4)) как о структурах данных, которые представляют математическое выражение. Если вы попросите пролог достичь цели f (A-1, B) , он попытается найти такой атом f (A-1, B). или правило (f (A-1, B): - Z), Z. , которое будет объединено для «успеха».
is / 2 пытается объединить первый аргумент с результатом интерпретации второго аргумента как выражения. Рассмотрим eval / 2 как вариант is / 2 :

eval(0, 1-1). eval(0, 2-2). eval(1,2-1).
eval(Y, X-0):- eval(Y, X).
eval(Y, A+B):- eval(ValA, A), eval(ValB, B), eval(Y, ValA + ValB).
eval(4, 2*2).
eval(0, 0*_). eval(0, _*0).
eval(Y, X*1):- eval(Y, X).
eval(Y, 1*X):- eval(Y, X).
eval(Y, A*B):- eval(ValA, A), eval(ValB, B), eval(Y, ValA * ValB).

Причина, по которой f (X, 120) не работает, проста > / 2 работает только тогда, когда его аргументы связаны (т.е. вы не можете сравнивать что-то еще не определенное, например X , с чем-либо еще). Чтобы исправить это, вы должны разделить это правило на:

f(A,B) :- nonvar(A), A > 0, C is A-1, f(C, D), B is A*D.
f(A,B) :- nonvar(B), f_rev(A, B, 1, 1).

% f_rev/4 - only first argument is unbound.
f_rev(A, B, A, B). % solution 
f_rev(A, B, N, C):- C < B, NextN is (N+1), NextC is (C*NextN), f_rev(A, B, NextN, NextC).

Обновление : (исправлено f_rev / 4 )
Возможно, вас заинтересует решатель конечной области. Был вопрос об использовании таких вещей. Используя #> / 2 и # = / 2 , вы можете описать некоторые формулы и ограничения, а затем разрешить их. Но эти предикаты используют особые возможности некоторых систем прологов, которые позволяют связывать имя с некоторыми атрибутами, что может помочь сузить набор возможных значений путем пересечения ограничения. Некоторые другие системы (обычно такие же) позволяют изменять порядок целей обработки («приостановить»).
Также member (X, [1,2,3,4,5,6,7]), f (X, 120) , вероятно, делает то же самое, что и ваши «другие запросы».

Если вас интересуют логические языки в целом, вы можете также взглянуть на язык Карри (там все нечистые функции «приостанавливаются» до тех пор, пока не будет унифицировано значение, не определенное yed).

5
ответ дан 1 December 2019 в 22:56
поделиться

is / 2 очень низкоуровневый и ограниченный. Как вы правильно заметили, он не может использоваться во всех направлениях и, следовательно, не является истинным соотношением.

Для обратимой арифметики используйте решатели ограничений системы Prolog .

Например, руководство SWI-Prolog CLP (FD) содержит следующее определение n_factorial / 2 :

:- use_module(library(clpfd)).

n_factorial(0, 1).
n_factorial(N, F) :- N #> 0, N1 #= N - 1, F #= N * F1, n_factorial(N1, F1).

Следующие примеры запросов показывают, что его можно использовать во всех direction:

?- n_factorial(47, F).
F = 258623241511168180642964355153611979969197632389120000000000 ;
false.

?- n_factorial(N, 1).
N = 0 ;
N = 1 ;
false.

?- n_factorial(N, 3).
false.

Конечно, это определение все еще основывается на унификации, и поэтому вы не можете вставлять произвольные целочисленные выражения. Такой термин, как 2-2 (который равен - (2,2) в префиксной записи), не противоречит 0 . Но вы можете легко разрешить это, если переписываете это в:

:- use_module(library(clpfd)).

n_factorial(N, F) :- N #= 0, F #= 1.
n_factorial(N, F) :- N #> 0, N1 #= N - 1, F #= N * F1, n_factorial(N1, F1).

Пример запроса и его результат:

?- n_factorial(2-2, -4+5).
true .
7
ответ дан 1 December 2019 в 22:56
поделиться

Есть некоторые вещи, которые вы должны помнить при изучении Prolog:

  • Не существует неявного возвращаемого значения, когда вы вызываете предикат. Если вы хотите получить значение из вызова, вам нужно добавить дополнительные аргументы, которые могут быть использованы для "возврата" значения, второй аргумент в вашем f/2 предикате. Хотя этот предикат более многословен, он имеет то преимущество, что легко возвращает много значений.

  • Это означает, что автоматическая "оценка" аргументов в вызове на самом деле совершенно бессмысленна, поскольку нет значения для возврата, и она не выполняется. Поэтому нет вложенных вызовов, в этом отношении Пролог плоский. Поэтому, когда вы вызываете f(A-1, D), первым аргументом f/2 является структура A-1, или на самом деле -(A, 1), поскольку - является инфиксным оператором. Поэтому, если вы хотите получить значение из вызова foo в вызов bar, вы должны явно использовать переменную для этого, например:

    foo(..., X), bar(X, ...),

  • Поэтому вам нужен специальный предикат, который заставляет арифметическую оценку, is/2. Его второй аргумент - это структура, представляющая арифметическое выражение, которое он интерпретирует, оценивает и объединяет результат с первым аргументом, который может быть либо переменной, либо числовым значением.

  • Хотя в принципе вы можете выполнять обратные действия с большинством вещей, это не так. Обычно это возможно только для простых предикатов, работающих со структурами, хотя есть несколько очень полезных случаев, когда это возможно. is/2 не работает в обратном направлении, это было бы исключительным случаем, если бы работало.

Вот почему вам нужны дополнительные переменные C и D и вы не можете заменить C is A-1, f(C, D) на f(A-1,D).

(Да, я знаю, что в Прологе нельзя делать вызовы, а можно оценивать цели, но мы исходили из функциональной точки зрения)

3
ответ дан 1 December 2019 в 22:56
поделиться
Другие вопросы по тегам:

Похожие вопросы: