Reputation: 4975
I've written a generator which generates a string of digits given a description of them:
:- use_module(library(clpfd)).
some(_Int, 0) --> [].
some(Int, Count) --> {Count #> 0, Count1 #= Count - 1}, [Int], some(Int, Count1).
many([]) --> [].
many([some(X, N)|Rest]) --> some(X, N), many(Rest).
This works when run forward:
?- phrase(many([some(0, 3), some(1, 2)]), Some).
Some = [0, 0, 0, 1, 1] ;
false.
Well, it leaves a choicepoint, but at least it first gives the correct result. It loops forever when asked to generate a description for a given string of digits:
?- phrase(many([Some|Rest]), [0, 0, 0, 1, 1]).
OOPS
What am I doing wrong?
Upvotes: 2
Views: 66
Reputation: 22803
I'm going to give you a slightly "operational" perspective on what your problem is. Dijkstra forgive me.
The crux of the problem is that there is a way for you to do nothing in many//2
and then again in some//2
. Your first clause of many//2
is happy to eat none of the input. But, in your second clause, you sort of assume that some//2
is going to eat some of your input--but it doesn't have to, and then you are recurring back into your first clause of many//2
, which still doesn't have to eat any input. So you find yourself in a recursive call to many//2
, back in the second clause of many//2
once again, with precisely the same input as when you started. This is your loop!
The solution is to ensure that some//2
definitely does some work: the first clause has to go:
some(Int, 1) --> [Int].
some(Int, Count) --> {Count #> 0, Count1 #= Count - 1}, [Int], some(Int, Count1).
This is not as aggressive as you'd probably like in folding together some/2
structures, but it does work and it terminates:
?- phrase(many([Some|Rest]), [0, 0, 0, 1, 1]).
Some = some(0, 1),
Rest = [some(0, 1), some(0, 1), some(1, 1), some(1, 1)] ;
Some = some(0, 1),
Rest = [some(0, 1), some(0, 1), some(1, 2)] ;
Some = some(0, 1),
Rest = [some(0, 2), some(1, 1), some(1, 1)] ;
Some = some(0, 1),
Rest = [some(0, 2), some(1, 2)] ;
Some = some(0, 2),
Rest = [some(0, 1), some(1, 1), some(1, 1)] ;
Some = some(0, 2),
Rest = [some(0, 1), some(1, 2)] ;
Some = some(0, 3),
Rest = [some(1, 1), some(1, 1)] ;
Some = some(0, 3),
Rest = [some(1, 2)] .
Upvotes: 3