I have the below functions.
When I call it with final_filter([(2, 2)], R)
, it prints a lot of "2 2" pairs. When I comment get_all_sums(S, _)
it works fine, however if I test separately get_all_sums(4, R)
. I works also fine, what could be the problem?
get_all_sums_R(NR, IT, []):- IT > NR - 2.
get_all_sums_R(NR, IT, R):-
A is IT,
B is NR-IT,
A >= B,
NEXT_IT is IT + 1,
get_all_sums_R(NR, NEXT_IT, R_NEXT),
append([(A, B)], R_NEXT, R).
get_all_sums_R(NR, IT, R):-
A is IT,
B is NR-IT,
A < B,
NEXT_IT is IT + 1,
get_all_sums_R(NR, NEXT_IT, R).
get_all_sums(NR, R):-get_all_sums_R(NR, 2, R).
get_all_divisors(X, IT, []):-IT > X/2.
get_all_divisors(X, IT, R):-
IT =< X/2,
TMP_RES is mod(X, IT),
TMP_RES =:= 0,
NEXT_IT is IT + 1,
get_all_divisors(X, NEXT_IT, R_NEXT),
append([IT], R_NEXT, R).
get_all_divisors(X, IT, R):-
IT =< X/2,
NEXT_IT is IT + 1,
get_all_divisors(X, NEXT_IT, R).
get_all_products_R([], _, []).
get_all_products_R([H|L], NR, R):-
A is H,
B is NR/H,
A >= B,
get_all_products_R(L, NR, NEXT_R),
append([(A, B)], NEXT_R, R).
get_all_products_R([H|L], NR, R):-
A is H,
B is NR/H,
A < B,
get_all_products_R(L, NR, R).
get_all_products(NR, R):-
get_all_divisors(NR, 2, R_LEFT),
get_all_products_R(R_LEFT, NR, R).
single_element([_]).
final_filter([(A, B)|_], _):-
write(A),
P is A*B,
S is A+B,
get_all_products(P, PRODUCTS),
get_all_sums(S, _),
write(A),write(' '), write(B), write('\n'),
not(single_element(PRODUCTS)).
The central misunderstanding here is what termination means in Prolog. That is, universal termination. The query of interest is the following:
?- get_all_sums(4,R).
R = [(2,2)]█
Prolog's top-level shell offers you first a single answer. But you get more of them by entering ; or SPACE. So Prolog was not finished when showing you the first answer/solution. In fact:
?- get_all_sums(4,R).
R = [(2,2)]
; R = [(2,2),(3,1)]
; R = [(2,2),(3,1),(4,0)]
; R = [(2,2),(3,1),(4,0),(5,-1)]
; R = [(2,2),(3,1),(4,0),(5,-1),(6,-2)]
; R = [(2,2),(3,1),(4,0),(5,-1),(6,-2),(7,-3)]
; R = [(2,2),(3,1),(4,0),(5,-1),(6,-2),(7,-3),(8,-4)]
; R = [(2,2),(3,1),(4,0),(5,-1),(6,-2),(7,-3),(8,-4),(9,-5)]
; ... .
Do you really want to enumerate all negative sums? Looks infinite to me!
But let's concentrate on the non-termination property alone...
Maybe the system will stop after 8 solutions? Is there a better way to be sure? Simply "turn off" the display of solutions as follows:
?- get_all_sums(4,R), false.
Now we are no longer irritated by solutions/answers, and we can concentrate on the termination property alone. I will further add such goals
false
and more into your program, to localize the actual reason for non-termination. The resulting program is called a failure-slice. And no matter how I add false
goals, it always holds that: If the failure-slice does not terminate, then the original program does not terminate either. After a bit of trying, I get:
get_all_sums_R(NR, IT, []):- false, IT > NR - 2. get_all_sums_R(NR, IT, R):- NR = 4, A is IT, B is NR-IT, A >= B, NEXT_IT is IT + 1, get_all_sums_R(NR, NEXT_IT, R_NEXT), false,append([(A, B)], R_NEXT, R).get_all_sums_R(NR, IT, R):- false,A is IT,B is NR-IT,A < B,NEXT_IT is IT + 1,get_all_sums_R(NR, NEXT_IT, R). get_all_sums(NR, R):-get_all_sums_R(NR, 2, R), false. ?- get_all_sums(4, R), false.
So this little remaining part is responsible for non-termination. In order to fix this, you will have to add some goals here. To be sure: NR = 4
, A
increases by 1, B
decreases by one. And as long as A >= B
, this loop will continue.
You might be tempted to add somewhere a cut, or a once/1
. But this
will lead you only from one bug to the other. Better address the problem above.