Prolog - вернуть результат вместо печати в алгоритме

Я знаю, что в Прологе технически нет «возврата», но я не знал, как иначе сформулировать вопрос.

Я нашел пример кода алгоритма поиска маршрутов между станциями метро. Он работает хорошо, однако предполагается, что он просто печатает результат, поэтому его трудно расширить или, например, выполнить findall/3.

% direct routes
findRoute(X,Y,Lines,Output) :- 
    line(Line,Stations),
    \+ member(Line,Lines),
    member(X,Stations),
    member(Y,Stations),
    append(Output,[[X,Line,Y]],NewOutput),
    print(NewOutput).

% needs intermediate stop
findRoute(X,Y,Lines,Output) :- 
    line(Line,Stations),
    \+ member(Line,Lines),
    member(X,Stations),
    member(Intermediate,Stations),
    X\=Intermediate,Intermediate\=Y,
    append(Output,[[X,Line,Intermediate]],NewOutput),
    findRoute(Intermediate,Y,[Line|Lines],NewOutput).

line — это предикат с атомом и списком станций.
Например: line(s1, [first_stop, second_stop, third_stop])

Итак, что я пытаюсь сделать, так это избавиться от этого print в строке 11 и добавить дополнительную переменную в мое правило, чтобы сохранить результат для последующего использования. Однако я с треском провалился, потому что независимо от того, что я пытаюсь, он либо входит в бесконечный цикл, либо возвращает false.

Сейчас:

?- findRoute(first_stop, third_stop, [], []).
% prints [[first_stop,s1,third_stop]]

Хочу:

?- findRoute(first_stop, third_stop, [], R).
% [[first_stop,s1,third_stop]] is stored in R

person skamsie    schedule 25.03.2016    source источник
comment
Вам просто нужно добавить новый аргумент к findRoute/4 и избавиться от print(NewOutput). Очевидно, выберите имя для нового аргумента, которое еще не используется в предикате.   -  person lurker    schedule 25.03.2016


Ответы (1)


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

solve :-
     .... some goals ...
     compute(A),
     write(A).

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

  1. «решить» обязательно. Это не имеет смысла в декларативном языке, таком как Пролог, потому что вы можете использовать предикаты в нескольких направлениях.
  2. «вычислить» также обязательно.
  3. write/1 является побочным эффектом, и его вывод доступен только на системном терминале. Это не дает нам простого способа фактически проверить предикат.

Такие шаблоны всегда должны выглядеть примерно так:

solution(S) :-
     condition1(...),
     condition2(...),
     condition_n(S).

где condition1 и т. д. — это просто чистые цели, которые описывают, что означает, что S является решением.

При запросе

?- solution(S).

тогда привязки для S будут автоматически печататься на верхнем уровне. Позвольте верхнему уровню сделать печать за вас!

В вашем случае есть прямое исправление: просто сделайте NewOutput одним из аргументов и удалите последний побочный эффект:

route(X, Y, Lines, Output, NewOutput) :- 
    line(Line, Stations),
    \+ member(Line, Lines),
    member(X, Stations),
    member(Y, Stations),
    append(Output, [[X,Line,Y]], NewOutput).

Также обратите внимание, что я изменил имя на просто route/5, потому что предикат имеет смысл также, если все аргументы уже созданы, что полезно для тестирования и т. д.

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

Код будет выглядеть примерно так:

route(S, S, _) --> [].          % case 1: already there
route(S0, S, Lines) -->         % case 2: needs intermediate stop
    { line_stations(Line, Stations0),
      maplist(dif(Line), Lines),
      select(S0, Stations0, Stations),
      member(S1, Stations) },
    [link(S0,Line,S1)],
    route(S1, S, [Line|Lines]).

Удобно, что вы можете использовать это для описания конкатенации списков, не нуждаясь в append/3 так много. Я также сделал несколько других изменений, чтобы улучшить чистоту и удобочитаемость, и я оставляю выяснение точных различий в качестве простого упражнения.

Вы вызываете это, используя предикат интерфейса DCG phrase/2, используя:

?- phrase(route(X,Y,[]), Rs).

где Rs — найденный маршрут. Заметьте также, что я использую термины формы link/3 для обозначения звеньев маршрута. Хорошей практикой является использование специальных терминов, когда известна арность. Списки хороши, например, если вы заранее не знаете, сколько элементов вам нужно представить.

person mat    schedule 25.03.2016
comment
Спасибо за ответ, однако я тоже новичок и до сих пор не знаю, как это исправить. Я попробовал то же самое, что и ваш первый пример: route(X, Y, Lines, Output, NewOutput) :- , но я не знаю, как «обновить» второй случай с 5 аргументами. - person skamsie; 25.03.2016
comment
Я рекомендую вам просто сразу перейти к версии DCG, так как вам не нужно думать о таком количестве переменных. В простой версии Prolog вы можете просто расширить второе предложение новой переменной, скажем, Route, и расширить рекурсивный вызов route точно такой же переменной. Вы можете думать об этой переменной только как о потоке, чтобы позже сообщить о маршруте с ней на верхнем уровне. Однако обратите внимание, что этот способ решения задачи в любом случае довольно неудачен, потому что вам нужно append/3 так много. DCG позволяют обойтись без append/3. Более эффективно и более читабельно! - person mat; 25.03.2016
comment
да, ты прав... Не могу поверить, что это было так просто. Я, должно быть, даже пробовал что-то подобное, но наткнулся на некоторые опечатки, когда пролог просто возвращал false :). На данный момент для меня нотация dcg довольно продвинута, но и за это спасибо. - person skamsie; 25.03.2016
comment
Отлично, новичкам довольно необычно мыслить так декларативно, как вы. Вы определенно на правильном пути с этим вопросом! Кроме ofCourseTheUnreadableNamingConvention, где using_underscores_is_much_more_readable! - person mat; 25.03.2016
comment
хе-хе, я почему-то думал, что это стандарт в Прологе, потому что переменные всегда начинаются с прописных букв, а New_result выглядит довольно странно. Ну, я только что проснулся однажды утром (около недели назад), желая выучить пролог, и я должен сказать, что поражен тем, насколько просты некоторые вещи и насколько сложны «простые» :)). - person skamsie; 25.03.2016
comment
Верблюжий регистр действительно иногда используется для переменных, но не для предикатов. И даже для переменных вы чаще видите лучшие соглашения, такие как State0, State1, ... State для обозначения последовательных состояний. В вашем примере Route0, Route1 и Route будут хорошим выбором. Уж точно лучше Route и NewRoute. - person mat; 25.03.2016
comment
кашель \+ member( - person false; 26.03.2016