Справка Prolog - Не могу понять правило

У меня есть база данных, полная таких фактов, как:

overground( watfordjunction   , watfordhighstreet , 2 ).
overground( watfordhighstreet , bushey            , 3 ).
overground( bushey            , carpenderspark    , 3 ).
overground( carpenderspark    , hatchend          , 2 ).

Пример: от перекрестка Уотфорд до Уотфордхайстрит ехать 2 минуты.

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

isjourney( Station1 , Station2 ) :-
  overground( Station1 , _        , _ ) ,
  overground( _        , Station2 , _ ) ; 
  overground( Station2 , _        , _ ) ,
  overground( _        , Station1 , _ )
  .
isjourney( Station1 , Station2 ) :-
  overground( Station1 , Station3 , _ ) ,
  isjourney(  Station3 , Station2 )
  .
isjourney( Station1 , Station2 ) :-
  overground( Station4 , Station2 , _ ) ,
  isjourney(  Station1 , Station4 )
  .

(Извините за подчеркивания, у меня были проблемы с их вставкой)

В конце концов я придумал то, что отлично работает, однако мне удалось придумать это только после проб и ошибок, поэтому я не могу объяснить, как это работает или даже что он делает... Может ли кто-нибудь, имеющий опыт работы с прологом, объяснить мне что оно делает?


person JimmyK    schedule 13.01.2012    source источник
comment
Извините, все факты, которые я упомянул выше, должны были называться наземными, а не станционными, вот что происходит, когда у вас есть около 5 версий одного и того же файла xD. В любом случае, я внес правки, надеюсь, теперь все имеет смысл, наземный уровень определяется фактами.   -  person JimmyK    schedule 13.01.2012


Ответы (3)


Я предполагаю, что вы посещаете тот же курс, что и @DavidGregg. Мой ответ на https://stackoverflow.com/questions/8789021/basic-prolog-but-struggling/8794162#8794162 может вам помочь.

FWIW, переменная пролога _ является анонимной/"не имеет значения". Переменная с именем вроде ____ не является не анонимной/"все равно". Анонимная переменная унифицируется с чем угодно, и унификация не переносится, поэтому с учетом таких фактов, как:

number(1).
letter(a).

предикат, такой как

foo :- number(_) , letter(_).

получится, тогда как

foo :- number(____) , letter(____) .

не будет.

В вашем первом предложении

isjourney( Station1 , Station2 ) :-
  overground( Station1 , _        , _ ) ,
  overground( _        , Station2 , _ ) ; 
  overground( Station2 , _        , _ ) ,
  overground( _        , Station1 , _ )
  .

Вы уверены, что он связывается так, как вы думаете?

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

isjourney( Station1 , Station2 ) :-
  overground( Station1 , _        , _ ) ,
  overground( _        , Station2 , _ )
  .
isjourney( Station1 , Station2 ) :- 
  overground( Station2 , _        , _ ) ,
  overground( _        , Station1 , _ )
  .

Однако ваша основная мысль верна: маршрут между двумя станциями A и B существует, если

  • станция А существует, и
  • станция B существует, и
  • существует прямой маршрут между станцией А и некоторой промежуточной станцией X, и
  • маршрут существует между этой промежуточной станцией X и станцией B

Однако я полагаю, что вы упустили один случай: когда станция A и станция B находятся в непосредственной близости.

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

Я был бы склонен написать предикат что-то вроде

isjourney(A,B) :-
  station_exists(A) ,   % verify that station A exists
  station_exists(B) ,   % verify that station B exists
  (
    route_exists(A,B)   % verify that a route exists between A and B
    ;                   % in either direction
    route_exists(B,A)
  )
  .

route_exists(A,B) :- % 1st, check for directly adjacent stations
  overground(A,B,_) ,
  .
route_exists(A,B) :- % 2nd, check for indirect routes
  overground(A,T,_) ,
  route_exists(T,B)
  .

% =========================================================
% check for the existence of a station
% we want the cut to alternatives. A station exists or it doesn't
% we don't want backtracking to succeed by finding every instance
% of the station
% in the route map.
% =========================================================
station_exists(X) :- overground(X,_,_) , ! .
station_exists(X) :- overground(_,X,_) , ! .

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

Как отмечалось в моем ответе, указанном выше, решение по-прежнему уязвимо для бездонной рекурсии, если в графе существует цикл (если, например, станция A связана со станцией B, а станция B связана с обеими станциями C и A). Обнаружение таких циклов остается за вами.

person Nicholas Carey    schedule 13.01.2012

Ваш первый пункт неверен. Должен быть

isjourney(Station1, Station2):- 
  overground(Station1, Station2, _).

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

isjourney(Station1,Station2):-
    (overground(Station1,Station3,_), isjourney(Station3,Station2)) ; 
    (overground(Station3,Station2,_), isjourney(Station1,Station3)) .

По сути, вы говорите, что существует путешествие, если наземная Станция 1 и Станция 2 преуспеют, или если есть промежуточная станция (Станция 3), для которой вы можете совершить путешествие от Станции 1 до Станции 3, а затем от Станции 3 до Станции 2 (или обратное путешествие). ).

person gusbro    schedule 13.01.2012

Я предполагаю, что вы пытаетесь ответить на то же, что и человек в этом вопросе.

Прежде всего, вы должны использовать свой ответ на вопрос 2:

Базовый случай, если есть прямое путешествие:

is_journey(Station1, Station2) :-
    q2(Station1, Station2).

Не базовый случай, вы должны использовать промежуточное звено:

is_journey(Station1, Station2) :-
    q2(Station1, StationTemp),
    is_journey(StationTemp, Station2).

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

Сначала мы вызываем предикат, который вызывает тот же предикат, но с 3 аргументами (последний отслеживает используемые станции):

is_journey(Station1, Station2) :-
    is_journey(Station1, Station2, [Station1]).

Затем мы используем тот же базовый случай:

is_journey(Station1, Station2, _) :-
    q2(Station1, Station2).

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

is_journey(Station1, Station2, Visited) :-
    q2(Station1, StationTemp),
    \+ member(StationTemp, Visited),
    is_journey(StationTemp, Station2, [StationTemp|Visited]).

Кстати, я не думаю, что ваш предикат OP верен, поскольку он только проверяет, действительно ли обе станции являются станциями, а не напрямую ли они связаны.

person m09    schedule 13.01.2012