Преобразование предложения создает бесконечный цикл - но как?

Я не могу понять, где это идет не так. Обратите внимание, что я очень плохо знаком с Прологом и уверен, что что-то упустил - просто не знаю, что это может быть. Может ли кто-нибудь помочь мне, пожалуйста?

Спасибо, вот мой код:

printSentence([]).   
printSentence([W|[]]) :-
    write(W),
    write('.'),
    nl.  
printSentence([W|R]) :-
    write(W),
    write(' '),
    printSentence(R).

transform([], Result).  
transform([Word|Rest], Result) :-
    replace(Word, Replacement),
    append(Result, Replacement, NewResult),
    transform(Rest, NewResult).

replace(my, your).
replace(i, you).
replace(you, me).
replace(am, are).
replace(Word, Word).

test :-
    X = [you, are, my, only, hope],
    transform(X, Result),
    printSentence(Result).

2 ответа

Решение

Это должно работать. Обратите внимание, что у вас был один transform([],Result), Кроме того, append не работает так, как вы пытались его использовать, но в целом вы были на правильном пути.

transform([], []).

transform([Word|Rest], [Replacement|RestOfResult]) :-
    replace(Word, Replacement),
    transform(Rest, RestOfResult).

Ответ @Junuxx - это один шаг к решению; в вашей программе есть еще одна проблема. Но первый шаг назад: @Junuxx обнаружил проблему и исправил ее. Ницца. Но как вы можете обнаружить такую ​​проблему? На самом деле, вы спросили " бесконечный цикл - но как?"

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

Давайте вернемся к вашей программе. Если вы загрузите его, вы получите сообщение вроде:

Предупреждение: /usager/SO/paranoid.pl:13:
    Синглтон-переменные: [Результат]

Что уже дает вам подсказку о том, что, скорее всего, неправильно. Увы, это не ваша самая большая проблема на данный момент. Ваша самая большая проблема в том, что цель test петля!

Локализация не прекращения

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

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

Другой способ - добавить цели false в вашу программу. Помните, ваша программа уже зациклена, поэтому такие дополнительные цели не причинят вам большого вреда. Зачем вандализировать вашу программу с этими false цели, которые вы никогда не хотели писать в первую очередь? Это потому что они false цели помогут вам обнаружить виновника не прекращения в вашей программе, скрывая "не относящиеся к делу" части. Это так, благодаря следующему наблюдению:

Если срез (= ваша вандализованная программа) не завершается, то и исходная программа также не завершается.

В некотором смысле, ошибка-фрагмент является причиной, по которой ваша программа не завершается. Или, если выразить это более строго: до тех пор, пока вы не измените видимую часть в фрагменте сбоя; то есть, пока вы пытаетесь попытать счастья только путем изменения частей, которые не видны в срезе ошибки, проблема будет сохраняться! Гарантированы! Это не самая лучшая гарантия, но это лучше, чем быть слепым.

Вот то, что я получаю как часть неудачи. Я удалил printSentence/1 потому что он больше не используется во фрагменте. И я добавил определение append/3, Некоторые прологи предлагают append/3 как встроенный предикат, который вы не можете изменить. В этом случае используйте другое имя, например local_append/3 - просто не забудьте заменить все вхождения!

 append ([], Zs, Zs):- неверно.
добавить ([X|Xs], Ys, [X|Zs]):-
   добавить (Xs, Ys, Zs), ложь.

преобразование ([], Результат):- ложно.
преобразование ([Слово | Отдых], Результат):-
    заменить (слово, замена),
    append (Result, Replacement, NewResult), false,
    преобразование (Rest, NewResult).

заменить (мой, ваш):- ложь.
заменить (я, ты):- ложь.
заменить (ты, я).
заменить (am, are):- неверно.
заменить (Слово, Слово):- неверно.

тестовое задание:-
    X = [ты моя единственная надежда],
    преобразование (X, Результат), ложь,
    printSentence (Результат).

Когда я загружаю этот фрагмент ошибки, я получаю:

?- тестовое задание.
ОШИБКА: вне локального стека

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

Некоторые наблюдения: изначально transform/2 Раньше был рекурсивным. Теперь этого больше нет. Единственная рекурсия осталась в пределах append/3, Поэтому я сначала смотрю на цель append(Result, Replacement, NewResult) и я пытаюсь выяснить, какие переменные могут быть. Самым простым является третий аргумент: NewResult это единственный случай в нашем фрагменте, поэтому мы можем заменить его на _, Переменная второго аргумента Replacement всегда будет me, И первый аргумент (здесь я сейчас должен посмотреть на test/0) будет неопределенной переменной. Таким образом, мы должны рассмотреть цель append(_, me, _),

Просто беги append(_, me, _), false чтобы увидеть, что эта цель не заканчивается! Вы можете увидеть это также, проверив срез-срез. Вот и снова:

 append ([], Zs, Zs):- неверно.
добавить ([X|Xs], Ys, [X|Zs]):-
   добавить (Xs, Ys, Zs), ложь.

смотреть на Ys: Никому нет до этого дела, это просто "сдано". Только первый и третий аргументы могут гарантировать завершение!

Для получения дополнительной информации см. Тег fail-slice.


Хорошая печать

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


Другая проблема

Есть еще одна проблема с вашей программой. Бежать printSentence([you]), false чтобы увидеть это! Отступление назад и побочные эффекты не объединяются легко. Для новичка лучше всего избегать побочных эффектов все вместе. Посмотрите этот вопрос и ответ на пример, как устранить бесполезные побочные эффекты в задачах программирования. Почему бы не позвонить transform([you, are, my, only hope], Xs) или же maplist(replace,[you, are, my only, hope], Xs) напрямую? Это позволяет вам снова сосредоточиться на соответствующих частях!

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