Пролог, если еще синтаксис

Кажется, я не могу заставить работать свое if else.

  1. Джон, Фред и Гарри - мужчины, Мэри, Джули, Сьюзен и Энн - женщины.
  2. У Джона светлые волосы, а у Фреда и Гарри темные волосы.
  3. Джули и Сьюзан блондинки, Мэри и Энн брюнетки.
  4. Богатым является каждый, кто владеет золотом, в нашем примере Фред и Джули.
  5. Мужчина любит только самку и наоборот. Кроме того, Джон и Гарри любят богатых людей, Джон любит блондинку, а Фред любит брюнетку.
  6. И Мэри, и Джули нравятся темноволосые люди, Джули одновременно нравятся богатые люди.
male(john).
male(fred).
male(harry). 

female(mary).
female(julie).
female(susan).
female(anne).

hasblonde(X):-(male(X),X = john);(female(X),X = susan);(female(X),X = julie).

hasdarkhair(X):-(male(X),X = harry);(male(X),X = fred).

hasbrunette(X):-(female(X),X = mary);(female(X),X = anne).

isrich(X):-(female(julie),X=julie);(male(fred),X=fred).


likes(male(X),female(Y));likes(female(X),male(Y)):-likes(X,Y).    
likes(X,Y):-
 ((X==julie)->
    ((hasdarkhair(Y))->
        (female(X), male(Y));
        male(X));
    female(X),male(Y));
 ((X==julie)->
    ((isrich(Y))->
        (female(X), male(Y));
        male(X));
    female(X),male(Y));
 ((X=mary)->
    ((hasdarkhair(Y))->
        (female(X), male(Y));
        male(X));
    female(X),male(Y));
 ((X=john)->
    ((isrich(Y))->
        (female(X), male(Y));
        female(X));
    male(X),female(Y));
((X=harry)->
    ((isrich(Y))->
        (female(X), male(Y));
        female(X));
    male(X),female(Y));    
 ((X=fred)->
        ((hasbrunette(Y))->
            (female(X), male(Y));
            female(X));
    male(X),female(Y)).

я подумал (Заявление) -> (если истина, запустите эту инструкцию); (если ложь, запустите эту инструкцию). был правильным подходом к этому в Прологе. Почему это не имеет значения, для чего я пишу

likes(MaleName,FemaleName) 
likes(FemaleName,MaleName)

он возвращает истину?


person SuperCell    schedule 21.07.2015    source источник


Ответы (5)


Основываясь на ответе CapelliC, поскольку, по-видимому, его ответ был недостаточно ясным. Что касается синтаксиса if-else и его использования, см. В конце ответа.

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

person(john).
person(fred).
person(harry).
person(mary).
person(julie).
person(susan).
person(anne).

Хорошо. Теперь мы хотим констатировать, что некоторые из них - мужчины, а некоторые - женщины.

% John, Fred and Harry are men, Mary, Julie, Susan and Anne are women.
male(john).
male(fred).
male(harry).

female(mary).
female(julie).
female(susan).
female(anne).

Это еще две таблицы фактов. Теперь вы хотите добавить в свою базу данных информацию об их цвете волос:

% John has blonde hair while Fred and Harry have dark hair.
% Julie and Susan are blonde, Mary and Anne are brunette.
person_hair(john, blond).
person_hair(fred, dark).
person_hair(harry, dark).
person_hair(julie, blond).
person_hair(susan, blond).
person_hair(mary, dark).
person_hair(anne, dark).

Это таблица с двумя столбцами, если хотите: первый - это человек, второй - описание цвета волос. Слово «брюнетка» обычно используется для описания темноволосой женщины, поэтому мы могли бы добавить правило, которое гласит:

% A brunette is a female with dark hair
brunette(X) :-
    female(X),
    person_hair(X, dark).

У некоторых людей есть золото, и владение золотом в нашей программе делает человека богатым:

person_owns(fred, gold).
person_owns(julie, gold).

is_rich(X) :-
    %person(X),
    person_owns(X, gold).

В нашей строго гетеросексуальной программе мужчинам нравятся женщины, а женщинам нравятся мужчины:

person_likes(M, F) :-
    male(M),
    female(F).
person_likes(F, M) :-
    female(F),
    male(M).

Как вы можете подсчитать, это дает нам 3 x 4 + 4 x 3 = 24 возможных решения для person_likes(A, B) без каких-либо дополнительных ограничений:

?- bagof(A-B, person_likes(A, B), R), length(R, Len).
R = [john-mary, john-julie, john-susan, john-anne, fred-mary, fred-julie, fred-susan, fred-anne, ... - ...|...],
Len = 24.

Это очень общее правило: оно описывает отношения между свободными переменными, что отличает его, например, от нашего отношения person_owns/2. Это действительно полезно? Почему нет:

is_heterosexual(H) :-
    person(H).

Но это только говорит о том, что каждый человек в нашей программе гетеросексуален; это не позволяет нам вывести правило, согласно которому гетеросексуал - это тот, кому нравится противоположный пол. Может быть, даже лучше переименовать его, чтобы лучше выразить его значение (и я буду использовать конструкцию if-then-else, чтобы показать, как это обычно делается):

opposite_sex(X, Y) :-
    (   male(X)
    ->  female(Y)
    ;   female(X)
    ->  male(Y)
    ).

Для наших целей это могло быть так же хорошо, как и выше:

opposite_sex(M, F) :-
    male(M), female(F).
opposite_sex(F, M) :-
    male(M), female(F).

Таким образом, мы можем написать правило person_likes/2 с общим предварительным условием, гласящим, что другой должен быть противоположного пола:

person_likes(X, Y) :-
    opposite_sex(X, Y),
    fits_personal_taste(X, Y).

Теперь мы можем устанавливать правила для личных вкусов каждого человека. Для Джули:

fits_personal_taste(julie, X) :-
    is_rich(X),
    person_hair(X, dark).

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

% Anyone (male) would fit Anne's tastes
fits_personal_taste(anne, _).

Было бы лучше, если бы вместо этого у нас была таблица с записями для каждого человека, у которого есть есть предпочтения, например:

person_preferences(julie, [is_rich, person_hair(dark)]).
person_preferences(harry, [is_rich]).
% and so on

Это позволило бы нам написать fits_personal_taste/2 что-то вроде этого:

fits_personal_taste(X, Y) :-
    (   person_preferences(X, Ps)
    ->  maplist(fits_preference(Y), Ps)
    ;   true
    ).

Это предполагаемое использование конструкции if-else в Прологе: исключающее ИЛИ.

Если у человека есть предпочтения, проверьте, подходит ли кандидат им всем; в противном случае - успешно.

Но как бы fits_preference/2 выглядело? В качестве первого аргумента он принимает человека, термин с предпочтением во втором, и должен как-то проверять, удовлетворяется ли это предпочтение для этого человека. Одним из хакерских решений было бы использовать так называемый оператор Univ =.., чтобы взять член в форме person_hair(Color), создать член в форме person_hair(Person, Color) и назвать его:

fits_preference(Person, Preference) :-
    Preference =.. [F|Args],
    Preference1 =.. [F,Person|Args],
    call(Preference1).

Может быть, лучше, если person_preferences будет напрямую сопоставлять человека с вызываемыми терминами:

person_preferences(julie, P, [is_rich(P), person_hair(P, dark)]).
person_preferences(harry, P, [is_rich(P)]).
% and so on

При этом fits_personal_taste/2 становится:

fits_personal_taste(X, Y) :-
    (   person_preferences(X, Y, Ps)
    ->  maplist(call, Ps)
    ;   true
    ).

Когда person_preferences/3 вызывается в части условия оператора, каждое предпочтение в списке предпочтений привязано к конкретному человеку; Затем мы звоним каждому, чтобы проверить, можно ли доказать, что это соответствует действительности в нашей программе.

Наконец, вспомогательный предикат possible_pair/2, который утверждает, что оба человека должны нравиться друг другу:

possible_pair(X, Y) :-
    person_likes(X, Y),
    person_likes(Y, X),
    X @< Y.

Последняя строка гарантирует, что мы не получим одну и ту же пару дважды, постулируя, что эти два человека должны быть в строгом порядке.

Таким образом я получаю:

?- bagof(A-B, possible_pair(A, B), R).
R = [fred-mary, anne-fred].

Или, для списка однонаправленных лайков,

?- bagof(A-B, person_likes(A, B), R), write(R).
[john-julie,fred-mary,fred-anne,harry-julie,susan-john,anne-john,mary-fred,julie-fred,susan-fred,anne-fred,mary-harry,susan-harry,anne-harry]
R = [john-julie, fred-mary, fred-anne, harry-julie, susan-john, anne-john, mary-fred, julie-fred, ... - ...|...].
person Community    schedule 21.07.2015
comment
Отлично. Моя программа уже работает безупречно, но эта организация слишком хороша, чтобы обойтись без нее. Я собираюсь переделать его еще раз. Спасибо за ясность. - person SuperCell; 21.07.2015
comment
У меня нет 15 очков репутации, чтобы голосовать, но когда я это сделаю, я вернусь сюда. - person SuperCell; 21.07.2015
comment
А как насчет проверок безопасности на предмет наличия достаточных экземпляров аргументов? - person false; 22.07.2015
comment
@false Например? Как я имел в виду, это должна быть программа, которая имеет два предиката в качестве интерфейса: person_likes/2 и possible_pair/2, и базу данных с такими предикатами, как person_hair/2 или person_preferences/3. Остальные предназначены для личного пользования. - person ; 22.07.2015
comment
Что касается possible_pair/2. - person false; 22.07.2015
comment
@false Я не уверен. Он либо завершится успешно с решением (ями), либо потерпит неудачу, даже с самым общим запросом. Что мне не хватает? - person ; 22.07.2015
comment
@false Поскольку все гетеросексуальны, человек никак не может нравиться себе. Я не могу придумать никакой другой причины, почему бы это не прекратилось. Я точно знаю, что ваши комментарии всегда не зря. - person ; 22.07.2015
comment
@false или, иначе говоря, person_likes(A, B) является полудетерминированным для того, что я вижу. - person ; 22.07.2015

Прежде чем перейти к вашему вопросу, я хотел бы отметить, что в вашем коде есть несколько проблем:

О синтаксисе: p1 ; p2 :- p3. недействителен.

?- [user].
p1;p2:-p3.
ERROR: user://1:9:
    No permission to modify static procedure `(;)/2'
    ...

Пролог использует особую «кодировку» логических формул, так называемые предложения Хорна

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

О моделировании проблемы: я считаю важным минимизировать синтаксические различия между формулами «естественного языка» и вычислительными. Каждое изменение должно быть отлажено :-)

Итак, почему бы не определить

rich(Person) :- owns(Person, gold).
owns(fred, gold).
owns(julie, gold).

Вы можете найти много вопросов и ответов о головоломке Zebra, я не буду повторять их здесь, поэтому, пожалуйста, посмотрите [zebra-puzzle] в поле поиска Stack Overflow. Вы увидите, что if/then/else никогда не требуется - по уважительным причинам. На Прологе есть гораздо более простые способы выразить такие основные проблемы.

person CapelliC    schedule 21.07.2015
comment
Хороший ответ (+1). Кстати, это даже не похоже на вопрос типа головоломки Zebra, это больше о поиске правильного способа моделирования данных, а именно о том, каковы факты, каковы правила. Ваш пример с rich/1 и owns/1 идет правильно. - person ; 21.07.2015
comment
вы можете вставлять теги с синтаксисом: [tag:zebra-puzzle] - person mat; 21.07.2015

Итак, я исправил это, но теперь мне нужно переделать это с тем, что вы сказали выше. Этот способ работает, но кажется, что он дает только первый ответ и не ищет другого. Пример: вывод

1 ?- likes(julie,X).
X = harry ;
false.

Редактирование программы:

likes(male(X),female(Y)):-likes(X,Y).
likes(female(X),male(Y)):-likes(X,Y).
likes(X,Y):-
 ((X=julie)->
    ((hasdarkhair(Y))->
        (female(X), male(Y));
        male(X));

  ((X=julie)->
    ((isrich(Y))->
        (female(X), male(Y));
        male(X));

 ((X=mary)->
    ((hasdarkhair(Y))->
        (female(X), male(Y));
        male(X));
    female(X),male(Y))));

 ((X=john)->
    ((isrich(Y))->
        (male(X),   female(Y));
        female(X));
  ((X=john)->
    ((isblonde(Y))->
        (male(X),   female(Y));
        female(X));
 ((X=harry)->
    ((isrich(Y))->
        (male(X),   female(Y));
        female(X));
 ((X=fred)->
    ((hasbrunette(Y))->
        (male(X),   female(Y));
        female(X));
    male(X),female(Y))))).

Когда он должен вернуться

X=Harry
X=Fred

потому что

likes(julie,fred)
true 

// возвращает

person SuperCell    schedule 21.07.2015
comment
Если «оператор» / then / else был введен специально, чтобы избежать выработанных альтернатив, Prolog делает это по умолчанию. Возможно, подумайте о Prolog как о `` программируемом '' SQL на более богатых доменах, чем атомарные значения ... - person CapelliC; 21.07.2015

Изменение кода на:

male(john).
male(fred).
male(harry).
female(mary).
female(julie).
female(susan).
female(anne).

hasblonde(X):-(male(X),X = john);(female(X),X = susan);(female(X),X = julie).
hasdarkhair(X):-(male(X),X = harry);(male(X),X = fred).
hasbrunette(X):-(female(X),X = mary);(female(X),X = anne).

isrich(X):-(female(julie),X=julie);(male(fred),X=fred).


likes(X,Y):-((X=julie),(hasdarkhair(Y);isrich(Y)),(female(X),male(Y))).
likes(X,Y):-((X=mary),hasdarkhair(Y),female(X),male(Y)).
likes(X,Y):-((X=john),(isrich(Y);hasblonde(Y)),male(X),female(Y)).
likes(X,Y):-((X=harry),isrich(Y),male(X),female(Y)).
likes(X,Y):-((X=fred),hasbrunette(Y),male(X),female(Y)).
likes(X,Y):-((X=susan);(X=anne)),((male(X),female(Y));(female(X),male(Y))).
    ownscar(john).
love(X,Y):-likes(X,Y),likes(Y,X).

Это лучше?

person SuperCell    schedule 21.07.2015
comment
Но я использую точку с запятой как оператор или, чтобы избежать повторения кода. Можете ли вы привести мне пример, без чего можно обойтись лучше? - person SuperCell; 21.07.2015

Я бы подошел к проблеме, определив набор людей, каждый из которых имеет необходимый набор атрибутов (пол, цвет волос и финансовое положение) и набор дополнительных предпочтений (пол, цвет волос и финансовое положение). Мы будем придерживаться соглашения о том, что состояние безразлично для определенного подобия указывается анонимной переменной _:

person( john  , is( male   , blonde   , poor ) , likes( female , blonde   , rich ) ) .
person( fred  , is( male   , brunette , rich ) , likes( female , brunette , _    ) ) .
person( harry , is( male   , brunette , poor ) , likes( female , _        , rich ) ) .

person( mary  , is( female , brunette , poor ) , likes( male   , brunette , _    ) ) .
person( julie , is( female , blonde   , rich ) , likes( male   , brunette , rich ) ) .
person( susan , is( female , blonde   , poor ) , likes( male   , _        , _    ) ) .
person( anne  , is( female , brunette , poor ) , likes( male   , _        , _    ) ) .

Это позволяет очень просто определить, присутствует ли влечение:

likes( P1 , P2 ) :-
  person( P1 , _            , likes(G2,H2,S2) ) ,
  person( P2 , is(G2,H2,S2) , _               )
  .

Если вы хотите показать взаимное притяжение, вы можете просто немного расширить это:

mutual_attraction( P1 , P2 ) :-
  person( P1 , is(G1,H1,S1) , likes(G2,H2,S2) ) ,
  person( P2 , is(G2,H2,S2) , likes(G1,H1,S1) )
  .

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

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

person Nicholas Carey    schedule 22.07.2015
comment
Это тоже хороший способ приблизиться к нему (+1). Разница в том, что вы не нормализуете данные, а явно описываете достоинства и недостатки подхода. - person ; 22.07.2015