4

In the code given below, there is the ! (cut) that prunes the choice point for efficiency. I am pretty certain that the reverse predicate and the agent_do_moves predicate are essential.

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).
repeat
  • 18,496
  • 4
  • 54
  • 166
SDG
  • 2,260
  • 8
  • 35
  • 77
  • What exactly is your question? Do you need an explanation of how `!` works in general? – lenz Jun 13 '15 at 23:55
  • @Lenz yes, I am a bit confused as to why an essential part of the predicate is being cut for efficiency. – SDG Jun 14 '15 at 00:48
  • I take it you didn't write this predicate yourself, but rather you are trying to understand somebody else's code. In short: the cut doesn't cut off `reverse/2` and `agent_do_moves/2`, but rather it cuts off *alternative solutions* to `agent_current_position/2` and `solve_task_a/6`. So once these two have been proven once, they won't be reconsidered for other possible solutions (no backtracking). – lenz Jun 14 '15 at 10:07
  • @lenz No I didn't write these predicates, and I was just confused as `agent_current_position` does not give more than one answer. – SDG Jun 14 '15 at 12:06

1 Answers1

7

The cut in above examples has the following effect:

Ideally, it commits the search which might happen within solve_task_a/6 to the first answer found. This frees resources for finding further answers which improves space consumption.

Scope problems

However, at the same time, it might also hide further answers to agent_current_position/2. Of course, it does not make much sense to have further answers for this goal, but it might be an error that happens to sleep for a while, only to become active but still undiscovered in the worst possible situation.

For this reason, it would be preferable to write instead of the cut

    ...,
    once( solve_task_a( ... ) ),
    ...

This limits the scope precisely to what you want to express.

Steadfastness problem

But this is not the only possible source of problems. I see this variable Cost. Will it be instantiated when you call solve_task(Task, Cost) or not? I could do a lot of guessing here. But at least this variable might influence the answer Prolog will commit to. So solve_task(Task, 99) and solve_task(Task, Cost), Cost = 99 might produce different answers. In fact, the latter might even fail. Predicates that have such problems are said to lack steadfastness.

To illustrate how steadfastness is easily lost in such a situation consider this (runnable) sketch of your (already improved) program:

solve_task(Task,Cost):-
    % agent_current_position(oscar,P),
    once(solve_task_a(Task,[b(0,0,P)],[],R,Cost,_NewPos)),
    true.

solve_task_a(_, _, _, _, 20, _).
solve_task_a(_, _, _, _, 30, _).

Now

?- solve_task(a, Cost).
   Cost = 20.
?- solve_task(a, 30).
   true.
?- solve_task(a, Cost), Cost = 30.
   false.

There would be an easy way out of this problem by cleanly testing variable Cost, e.g. Cost >= 0 which produces an instantiation error should Cost be an uninstantiated variable. But if you want (as you indicate in your comment) to determine the cost, you need to put it like so:

solve_task(Task,Cost):-
    % agent_current_position(oscar,P),
    once(solve_task_a(Task,[b(0,0,P)],[],R,CostX,_NewPos)),
    CostX = Cost
    true.

In this manner we can be sure that Cost cannot influence the outcome of solve_task_a/6 (euh - provided there is no aliasing between Cost and Task - but let's assume that for the moment). One says also that output unifications are put behind the commit.

Many people will tell you that such extra care is not needed, as you will never use solve_task(Task, Cost) with a given cost. That might be the case, but are you sure you will remember it? And are you sure the source code will remember it (without any dynamic check)? Such implicit assumptions easily accumulate to a degree were your mental capacities are overburdened.

There is not always an easy way out. But quite often it is possible to stick to logical purity . In that case, you do not have to remember any such assumptions.


In any case, I would recommend that you do not go into these parts of Prolog for the moment. Rather stick to , , and other clean, monotonic programs preserving . There is a lot to learn!

false
  • 10,264
  • 13
  • 101
  • 209
  • perfect answer once again. The fact that there might be more answers to `agent_current_position` and even if there were, cutting them wouldn't be for efficiency but for accuracy, was baffling me – SDG Jun 14 '15 at 11:59
  • to answer your question, `Cost` is instantiated after calling `solve_task`, so I guess it is steadfast? This code isn't mine and I am currently doing an assignment. Therefore, I unfortunately do not have a choice about what I can learn. – SDG Jun 14 '15 at 12:02