Search code examples
listprolog

acumulating elements in a list


So my problem is this I have a predicate called espaco_fila(F, E) in which F is a list that contains elements and #, L is the resulting list without the #.

Example:

My output:

?- espaco_fila([1,2,3,#,1,2,3,4], E).
E = [1,2,3];
false.

?- espaco_fila([1,2,3,#,1,2,#,3,4,5], E).
E = [1,2,3];
false.

?- espaco_fila([1,2,3], E).
E = [];
false.

Correct output:

?- espaco_fila([1,2,3,#,1,2,3,4], E).
E = [1,2,3];
E = [1,2,3,4];
false.

?- espaco_fila([1,2,3,#,1,2,#,3,4,5], E).
E = [1,2,3];
E = [3,4,5];
false.

?- espaco_fila([1,2,3], E).
E = [1,2,3];
false.

But instead, I'm getting the 1st list as a result, instead of retroceding and getting both lists and an empty list if I only type numbers and I can't seem to figure out why.

Program:

espaco_fila(F, E) :- espaco_fila(F,E,[],[]).

espaco_fila([],ACP,ACP,_).
espaco_fila([P|R],E,ACP,ACE) :-
   P \== #,
   append(ACE,[P],NACE),
   espaco_fila(R,E,ACP,NACE).
espaco_fila([P|R],E,ACP,ACE) :-
   P == #,
   length(ACE,COMP),
   COMP >= 3,!,
   append(ACP,ACE,NACP),
   espaco_fila([],E,NACP,[]).
espaco_fila([P|R],E,ACP,ACE) :-
   P == #,
   length(ACE,COMP),
   COMP < 3,!,
   espaco_fila(R,E,ACP,[]).

Really any help would be appreciated.


Solution

  • This

    espaco_fila([P|R],E,ACP,ACE) :- P == #,
                                    length(ACE,COMP),
                                    COMP >= 3,!,
                                    append(ACP,ACE,NACP),
                                    espaco_fila([],E,NACP,[]).
    

    ... is the success branch.

    I can't fully judge whether it is correctly coded, but it doesn't stop.

    It continues right on with a recursive call.

    Instead you want it to stop with success, and allow the user to ask for more solutions using backtracking.

    You thus have to have two branches, one which stops and an alternate one with the same guard that is tried on backtracking:

    espaco_fila(F,Result) :- espaco_fila(F,[],Result).
    
    % at list end, the result is what's in the accumulator
    
    espaco_fila([],Acc,Acc). 
    
    % not on an #, accumulate, recurse
    
    espaco_fila([R|Rs],Acc,Result) :- 
       R \= #,
       !,      
       append(Acc,[R],NextAcc),
       espaco_fila(Rs,NextAcc,Result).
    
    % hit a # and accumulator has > 3 atoms, then the result is what's in
    % the accumulator
    
    espaco_fila([#|R],Acc,Acc) :-
       length(Acc,L),
       L >= 3.
    
    % ALTERNATIVELY (on retry or length too small), the (next) result is
    % what's being generated by a recursive call. This behaves as if the
    % clause above had never even happened (really as in Greg Egan's "Quarantine")
    
    espaco_fila([#|R],_,Result) :-
       espaco_fila(R,[],Result).
    
    

    I'm not 100% sure this works, you want to write the unit tests for this. Unit tests and Test-Driven Development are good!

    Note: use unification = and impossible-to-unify \= instead of structural equivalence == and structural unequivalence \==.