Оценка бихевиористического подхода

Одна из проблем, возникающих при проведении в жизнь бихевиористического подхода к программированию, заключается в том, что текст программы при этом получается сложным и не подчиненным "дисциплине" программирования. При таком подходе можно написать программу, которую будет столь же трудно понять, как и худшие в этом отношении программы, написанные на процедурных языках. Именно это произойдет, если без разбора пользоваться предикатами, обладающими побочными эффектами поведения. Реляционный подход и подход к программированию с позиций потока данных обладают большей "дисциплиной", что определяет более стройный стиль мышления программиста и в значительной степени защищает программу от появления запутанных конструкций. Следовательно, рекомендуется прибегать к бихевиористическому подходу только в тех случаях, когда два других подхода к программированию оказываются совершенно неадекватными. Если Вам все же придется воспользоваться бихевиористическим подходом, то попытайтесь ограничить сферы действия побочных эффектов, сводя их в процедуры общего назначения по типу процедуры "найти или спросить", и не включайте их во фразы, выполняющие специфические действия по решению конкретных задач.

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

Библиографические заметки

Метод управления поиском при помощи списка посещенных узлов (как это делается в программе "тснр_путешествовать3") подробно исследуется в книге [79].

Идея создания процедуры "найти_или_спросить", приведенной в данной главе, возникла у автора после знакомства со средством опросить_пользователя языка APES [38].

Упражнения

1. Предположим, что факты базы данных "путешествие" (см. разд. 4. 1) содержат еще по одному аргументу, который задает расстояние в милях между двумя городами. Напишите программу, которая вычисляет наиболее короткий путь между двумя городами, при этом за основу возьмите текст процедуры "тснр_путешествоватьЗ"

Существуют два подхода к решению этой задачи Один подход основывается на рекурсии, а второй — на поиске с возвратом

Рекурсивное решение

При рекурсивном подходе применение предиката "findall" позволяет собрать в одном списке все ответы на запрос к программе "тснр_путешествовать3". Далее ответы сортируются, чтобы найти самый короткий маршрут Во многих версиях языка Пролог имеется встроенный предикат "keysort" ("сортировка по ключу"), первым аргументом которого является список структур "-/2", а вторым (выходным) аргументом служит вырабатываемый данным предикатом список структур "-/2", отсортированных по значениям первого аргумента Символ "-“ является обозначением инфиксной операции. Приведем пример запроса к предикату "keysort":

|?— keysort ([6-привет, 3-м (нью_йорк, самолет, лос_анжелес),

2- [один]]. Х).

Х = [2- [один], 3-м (нью_йорк, самолет, лос_анжелес),

6-привет]

Для того, чтобы воспользоваться предикатом "keysort", необходимо так изменить процедуру "тснр_путешествовать3", чтобы она через четвертый аргумент возвращала структуру "-/2". Первым аргументом этой структуры будет расстояние в милях, а вторым - структура "м", описывающая путь между двумя городами. Предположим, что автомобильный маршрут между Ньюарком и Бронксом имеет длину в 25 миль, а автобусный маршрут между Бронксом и Куинсом — 10 миль. Тогда при запросе к процедуре "тснр_путешествовать3" должен выдаваться такой ответ

|?— тснр_путешествовать3 (ньюарк, куинс, М).

М = 35-м (автомобиль, бронкс, м (автобус, куинс))

После проведения сортировки списка ответов на запрос к процедуре "тснр_путешествовать3" ответ, располагающийся в начале списка, будет соответствовать наикратчайшему пути.

Решение при использовании поиска с возвратом

При реализации поиска с возвратом в процедуру "тснр_путешествоватьЗ" следует добавить дополнительный аргумент, содержащий сведения о расстоянии (в милях) для определенного маршрута между двумя городами, Нужно написать промежуточную процедуру, которую можно, скажем, назвать "наилучший_маршрут". У этой процедуры будут те же аргументы, что и у процедуры "тснр_путешествовать3". Процедура "наилучший маршрут" будет вызывать процедуру "тснр_путешествовать3" как одну из своих подцелей. В процедуру "наилучший_маршрут" необходимо ввести глобальную переменную_факт, которую можно, например, назвать "оптимум/2". Употребление этой переменной будет во многом похоже на использование факта "текущ_сумма" в программе "итог_окл" из разд. 3. 11. Первым аргументом факта "оптимум" является структура "м", описывающая маршрут между двумя городами, а вторым аргументом - количество миль для этого маршрута. Первое, что должна сделать процедура "наилучший_маршрут", - это добавить факт "оптимум", содержащий аргумент с очень большим расстоянием, превышающим возможный предел расстояний для данной задачи, например:

оптимум (_, 100000).

Затем процедура "наилучший_маршрут" должна выдать запрос к процедуре "тснр_путешествовать3" и сравнить полученное в результате выполнения этого запроса количество миль со значением расстояния, которое содержится в факте "оптимум". Если количество миль, полученное в результате запроса, будет меньше, чем это расстояние, то процедура должна удалить факт "оптимум" и добавить вместо него новый факт, в котором будут содержаться сведения о расстоянии и о новом маршруте, полученные в результате запроса. Процедура "наилучший_маршрут" должна провести такой поиск с возвратом по всем ответам на запрос к "тснр_путешествоватьЗ". Тогда после исчерпания множества ответов самым коротким будет маршрут, описываемый текущим фактом "оптимум".

Какое из двух решений приведет к созданию более читабельной программы? При каком алгоритме решения будет достигнута более высокая скорость выполнения программы? В какой версии программы будут меньше потребности в памяти7 Какой подход к программированию на языке Пролог (реляционный, с позиций потока данных, бихевиористический) будет в наибольшей степени соответствовать применяемому методу решения в каждом случае?

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

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

Соседние файлы в папке Гл.0,1,2,3,4,5,Предисловие