prologtime-complexitymeta-predicate

Finding a desired solution without using call_nth/2 multiple times or the use of findall/3?


As far as i understand, call_nth(:Goal, ?Nth) returns the Nth solution of Goal but at the same time it secretly calculates all the previous solutions (from 1st to (N-1)th) and simply ignores them. If we wish to compare the Xth with the (X+1)th solution for every X between 1 and N-1, call_nth becomes very costly, cause it basically keeps calculating solutions which have already been calculated in previous steps. I was wondering if there is a more efficient way to solve problems of this form, without using findall/3 of course.

(A typical example to this, would be the script below which finds the GOAL or the closest maximum to it, if GOAL doesn't exist in the predicates.)

num(10).
num(20).
num(30).
num(40).
num(50).

search_for_goal(GOAL,RESULT):-
    search_for_goal(GOAL,0,1,RESULT).

search_for_goal(GOAL,_,COUNTER,GOAL):-
    call_nth(num(GOAL),COUNTER),!.
search_for_goal(GOAL,CURRENT_MAX,COUNTER,RESULT):-
    call_nth(num(X),COUNTER),
    COUNTER2 is COUNTER+1,
    (  X=<CURRENT_MAX->
       search_for_goal(GOAL,CURRENT_MAX,COUNTER2,RESULT)
    ;  search_for_goal(GOAL,X,COUNTER2,RESULT)
    ).
search_for_goal(_,CURRENT_MAX,COUNTER,CURRENT_MAX):-
    \+call_nth(num(_),COUNTER).

Solution

  • Using SWI-Prolog, this can be achieved with Prolog Engines. To illustrate this, consider my own version of the findall/3 predicate which returns only even-numbered solutions.

    my_findall(Templ, Goal, List) :-
        setup_call_cleanup(
        engine_create(Templ, Goal, E),
        get_answers(E, List),
        engine_destroy(E)).
    
    get_answers(E, [H|T]) :-
        /* Skip next solution. */
        engine_next(E, _),
        /* Take next solution. */
        engine_next(E, H),
        !,
        get_answers(E, T).
    
    get_answers(_, []).
    

    Querying with:

    my_findall(X,member(X,[1,2,3,4]),Y)
    

    Returns only the n'th solutions where n is even.

    Y = [2, 4]