Как сокращение точек выбора в приведенном ниже коде делает его более эффективным (Пролог)?
В приведенном ниже коде есть !
(вырезать), который сокращает точку выбора для эффективности. Я уверен, что reverse
предикат и agent_do_moves
Предикат необходимы.
solve_task(Task,Cost):-
agent_current_position(oscar,P),
solve_task_a(Task,[b(0,0,P)],[],R,Cost,_NewPos),!, % prune choice point for efficiency
reverse(R,[_Init|Path]),
agent_do_moves(oscar,Path).
1 ответ
Разрез в приведенных выше примерах имеет следующий эффект:
В идеале, он фиксирует поиск, который может произойти в solve_task_a/6
чтобы первый ответ нашел. Это освобождает ресурсы для поиска дальнейших ответов, что улучшает потребление пространства.
Проблемы с областью
Однако, в то же время, это может также скрыть дальнейшие ответы на agent_current_position/2
, Конечно, не имеет особого смысла иметь дальнейшие ответы для этой цели, но это может быть ошибкой, которая может поспать некоторое время, только чтобы стать активной, но все еще не обнаруженной в наихудшей возможной ситуации.
По этой причине было бы предпочтительнее написать вместо вырезать
...,
once( solve_task_a( ... ) ),
...
Это ограничивает сферу именно тем, что вы хотите выразить.
Проблема стойкости
Но это не единственный возможный источник проблем. Я вижу эту переменную Cost
, Будет ли он создан, когда вы звоните solve_task(Task, Cost)
или нет? Я мог бы сделать много догадок здесь. Но, по крайней мере, эта переменная может повлиять на ответ, который примет Пролог. Так solve_task(Task, 99)
а также solve_task(Task, Cost), Cost = 99
может дать разные ответы. На самом деле, последний может даже потерпеть неудачу. Говорят, что предикатам с такими проблемами не хватает стойкости.
Чтобы проиллюстрировать, как стойкость легко теряется в такой ситуации, рассмотрим (выполнимый) эскиз вашей (уже улучшенной) программы:
solve_task (Task, стоимость): - %agent_current_position (oscar, P),один раз (solve_task_a(Task,[B (0,0, Р)],[],R, стоимость,_NewPos)), правда. solve_task_a (_, _, _, _, 20, _). solve_task_a (_, _, _, _, 30, _).
Сейчас
?- solve_task(a, Cost).
Cost = 20.
?- solve_task(a, 30).
true.
?- solve_task(a, Cost), Cost = 30.
false.
Там будет легкий выход из этой проблемы путем чистого тестирования переменной Cost
например, Cost >= 0
который вызывает ошибку инстанции Cost
быть необоснованной переменной. Но если вы хотите (как вы указали в своем комментарии) определить стоимость, вы должны указать это следующим образом:
solve_task (Task, стоимость): - %agent_current_position (oscar, P),один раз (solve_task_a(Task,[B (0,0, Р)],[],R, CostX,_NewPos)), CostX = Стоимость правда.
Таким образом, мы можем быть уверены, что Cost
не может повлиять на исход solve_task_a/6
(э-э - при условии, что нет псевдонимов между Cost
а также Task
- но давайте предположим, что на данный момент). Говорят также, что выходные унификации ставятся за коммит.
Многие люди скажут вам, что такая дополнительная помощь не нужна, так как вы никогда не будете использовать solve_task(Task, Cost)
с заданной стоимостью. Это может быть так, но вы уверены, что будете помнить это? И вы уверены, что исходный код запомнит это (без какой-либо динамической проверки)? Такие неявные предположения легко накапливаются до некоторой степени, если ваши умственные способности перегружены.
Там не всегда легкий выход. Но нередко можно придерживаться логической чистоты, логической чистоты. В этом случае вам не нужно запоминать такие предположения.
В любом случае, я бы порекомендовал вам не заходить в эти части Пролога на данный момент. Скорее придерживайтесь арифметики-преемника, clpfd и других чистых, монотонных программ, сохраняющих логическую чистоту. Есть чему поучиться!