Пролог без утверждений if и else

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

if a > b then 1
else if
   a == b then c
else
    -1;;

Это просто очень простая логика, которую я хочу преобразовать в пролог.

Так что здесь, где я очень запутался. Я хочу сначала проверить, если a > b, и если да, выведите 1. Я бы просто сделал:

sample(A,B,C,O):-
   A > B, 1,
   A < B, -1,
   0.

Это то, что я придумал. o быть выводом, но я не понимаю, как сделать вывод 1. Есть мысли, чтобы помочь мне лучше понять это?

Пройдя еще раз, я придумал это, но, похоже, это не правильно:

Greaterthan(A,B,1.0).
Lessthan(A,B,-1.0).
Equal(A,B,C).

Sample(A,B,C,What):-
    Greaterthan(A,B,1.0),
    Lessthan(A,B,-1.0),
    Equal(A,B,C).

Я направился вниз по правильному пути?

5 ответов

Решение

В вашем коде есть как синтаксические, так и семантические проблемы.

Предикаты начинаются с нижнего регистра, а запятая представляет соединение. То есть вы можете прочитать ваше предложение как

sample(A,B,C,What) if
    greaterthan(A,B,1.0) and lessthan(A,B,-1.0) and equal(A,B,C).

затем обратите внимание, что What Аргумент бесполезен, так как он не получает значения - он называется синглтоном.

Возможный способ написания дизъюнкции (т.е. ИЛИ)

sample(A,B,_,1) :- A > B.
sample(A,B,C,C) :- A == B.
sample(A,B,_,-1) :- A < B.

Обратите внимание на тест A < B охранять присвоение стоимости -1, Это необходимо, потому что Prolog выполнит все условия, если потребуется. Базовая конструкция, заставляющая Пролог избегать вычислений, которые, как мы знаем, не должны выполняться, это cut:

sample(A,B,_,1) :- A > B, !.
sample(A,B,C,C) :- A == B, !.
sample(A,B,_,-1).

В любом случае, я думаю, что вы должны использовать синтаксис if/then/else, даже во время обучения.

sample(A,B,C,W) :- A > B -> W = 1 ; A == B -> W = C ; W = -1.

TL;DR: нет.

Вы пытаетесь перевести известные вам конструкции с других языков программирования на Пролог. С предположением, что изучение Пролога означает, по сути, отображение одной конструкции за другой в Пролог. В конце концов, если все конструкции были отображены, вы сможете закодировать любую программу в Prolog.

Однако, делая это, вы упускаете суть Prolog в целом.

Пролог состоит из чистого монотонного ядра и некоторых процедурных украшений. Если вы хотите понять, чем Prolog так сильно отличается от других языков программирования, вам действительно стоит сначала изучить его ядро. А это значит, что вы должны игнорировать эти другие части. У вас очень много внимания, и если вы тратите свое время на прохождение всех этих немонотонных, даже процедурных конструкций, есть вероятность, что вы упустите его суть.

Итак, почему общий if-then-else (как было предложено несколькими ответами) такая проблематичная конструкция? Есть несколько причин:

  1. В общем случае это нарушает монотонность. В чисто монотонных программах Prolog добавление нового факта увеличит набор истинных утверждений, которые вы можете извлечь из него. Так что все, что было правдой до добавления факта, будет правдой после этого. Именно это свойство позволяет очень эффективно рассуждать по программам. Тем не менее, обратите внимание, что монотонность означает, что вы не можете смоделировать каждую ситуацию, которую хотите смоделировать. Подумайте о предикате childless/1 это должно быть успешным, если у человека нет ребенка. И давайте предположим, что childless(john). правда. Теперь, если вы добавите новый факт о john будучи родителем какого-то ребенка, он больше не будет childless(john) правда. Таким образом, существуют ситуации, которые по своей природе требуют некоторых немонотонных конструкций. Но есть много ситуаций, которые можно смоделировать в монотонной части. Придерживайтесь тех, кто в первую очередь.

  2. if-then-else легко приводит к трудно читаемому вложению. Просто посмотрите на свою программу if-then-else и попробуйте ответить "Когда результат будет -1"? Ответ: "Если ни то, ни другое a > b верно ни a == b это правда ". Долго, не так ли? Поэтому люди, которые будут поддерживать, пересматривать и отлаживать вашу программу, должны будут" заплатить ".

Из вашего примера не ясно, какие аргументы вы рассматриваете, если вы будете довольны целыми числами, подумайте об использовании library(clpfd) как это доступно в SICStus, SWI, YAP:

sample(A,B,_,1) :- A #> B.
sample(A,B,C,C) :- A #= B.
sample(A,B,_,-1) :- A #< B.

Это определение теперь настолько общее, что вы можете даже спросить

Когда будет -1 быть возвращенным?

?- sample(A,B,C,-1).
  A = B,
  C = -1,
  B in inf..sup
;
  A#=<B+ -1.

Так что есть две возможности.

Если вы действительно хотите попытаться понять язык, я рекомендую использовать первое предложение CapelliC:

sample(A, B, _,  1) :- A > B.
sample(A, B, C,  C) :- A == B.
sample(A, B, _, -1) :- A < B.

Я не согласен с CappeliC в том, что вы должны использовать синтаксис if / then / else, потому что таким образом (по моему опыту) легко попасть в ловушку перевода различных конструкций, заканчивая тем, что вы выполняете процедурное программирование в Prolog, без полного ворчания языка сам.

Вот некоторые дополнения к полезному ответу CapelliC:

Начиная, иногда легко ошибочно представить предикаты Пролога функционально. Они либо вообще не являются функциями, либо являются n-арными функциями, которые в качестве выходных данных дают только true или false. Тем не менее, я часто нахожу полезным забыть о функциях и просто подумать о предикатах с точки зрения отношений. Когда мы определяем предикат p/n мы описываем связь между n элементы, и мы назвали отношение p,

В вашем случае, похоже, что мы определяем условия для упорядоченного триплета, <A, B, C> где значение C зависит от отношения между A а также B, Есть три соответствующих отношения между A а также B (здесь, поскольку мы имеем дело с простым случаем, эти три являются исчерпывающими для рассматриваемого рода отношений), и мы можем просто описать, какое значение C должно иметь в трех случаях.

sample(A, B, 1.0)  :-
    A > B.
sample(A, B, -1.0) :-
    A < B.
sample(A, B, some_value) :-
    A =:= B.

Обратите внимание, что я использовал арифметический оператор =:=/2, Это более конкретно, чем ==/2, и это позволяет нам сравнивать математические выражения для численного равенства. ==/2 проверяет эквивалентность терминов: a == a, 2 == 2, 5+7 == 5+7 все верно, потому что эквивалентные термины стоят слева и справа от оператора. Но 5+7 == 7+5, 5+7 == 12, A == a все ложные, поскольку сравниваются сами термины, и в первом случае значения меняются местами, во втором мы сравниваем +(5,7) с целым числом, а в третьем мы сравниваем свободную переменную с атомом. Следующее, однако, верно: 2 =:= 2, 5 + 7 =:= 12, 2 + 2 =:= 4 + 0, Это позволит нам объединить A и B с вычисляемыми математическими выражениями, а не просто целыми числами или числами с плавающей точкой. Затем мы можем поставить такие вопросы, как

?- sample(2^3, 2+2+2, X).
X = 1.0 
?- sample(2*3, 2+2+2, X).
X = some_value.

CapelliC указывает, что когда мы пишем несколько предложений для предиката, мы выражаем дизъюнкцию. Он также с осторожностью отмечает, что этот конкретный пример работает как простая дизъюнкция только потому, что альтернативы по своей природе взаимоисключающие. Он показывает, как получить ту же исключительность, которая обусловлена ​​структурой вашего первого "если... тогда... еще, если... еще...", вмешиваясь в процедуру разрешения с разрезами. На самом деле, если вы обратитесь к документации swi-prolog для условного ->/2 вы увидите семантику ->/2 объяснил с разрезами, ! и дизъюнкты, ;,

Я спускаюсь на полпути между CapelliC и SQB при назначении контрольных предикатов. Я думаю, что вам стоит придерживаться определения таких вещей с помощью отдельных предложений, пока вы еще изучаете основы синтаксиса. Тем не мение, ->/2 это просто еще один предикат с некоторым синтаксическим сахаром, так что не стоит его бояться. Как только вы начнете мыслить реляционно, а не функционально или императивно, вы можете обнаружить, что ->/2 это очень хороший инструмент для краткого выражения шаблонов отношений. Я бы отформатировал свое предложение, используя предикаты управления таким образом:

sample(A, B, Out) :-
    ( A > B   -> Out = 1.0
    ; A =:= B -> Out = some_value
    ; Out = -1.0
    ).

Во-первых, напишите достойный предикат сравнения (потому что сравнение/3 досадно неверно с неосновными аргументами):

      compare2(C, X, Y) :-
    (   X == Y
    ->  C = (=)
    ;   C == (=)
    ->  X = Y
    % compare when safe
    ;   when((ground(X), ground(Y)), compare(C, X, Y))
    ).

Затем используйте его:

      sample(A, B, C, Z) :-
    compare2(Comp, A, B),
    sample_(Comp, C, Z).

% Uses first-argument indexing
sample_(=, C, C).
sample_(>, _C, 1).
sample_(<, _C, -1).

Результаты в swi-prolog:

      ?- sample(A, B, C, Z).
C = Z,
when((ground(A), ground(B)), compare(=, A, B)) ;
Z = 1,
when((ground(A), ground(B)), compare(>, A, B)) ;
Z = -1,
when((ground(A), ground(B)), compare(<, A, B)).

?- sample(A, B, C, 3).
C = 3,
when((ground(A), ground(B)), compare(=, A, B)) ;
false.

% No unwanted choicepoint
?- sample(5, 7, C, Z).
Z = -1.

% No unwanted choicepoint
?- sample(X, X, C, Z).
C = Z.

?- sample(5, X, C, -1).
C = -1,
when(ground(X), compare(=, 5, X)) ;
when(ground(X), compare(<, 5, X)).

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

Другие вопросы по тегам