Search code examples
prologlogic

After assigning a variable a value in Prolog, program ouptut changes from `yes` to `no`


I have a simple task of finding the position of an array in another array. BTW, I'm using GNU Prolog 1.4.5.

parse([], _) :- true. %parsed sub means that we found it
parse([SHead|STail], [Head|Tail]) :-
    write(SHead), write(' '), write(STail), write(' | '), write(Head), write(' '), write(Tail), nl,
    (
        SHead =:= Head ->
            parse(STail, Tail)
        ;
            false
    )
.

findsub([_|_], [], _) :- false.    %unparsed sub & empty string means there is no sub
findsub([SHead|STail], [Head|Tail], Pos) :-
    (
        var(Pos) -> Pos = 0 ; true %first-time init
    ),
    XPos is Pos + 1,
    (
        SHead =:= Head ->
            (
                parse(STail, Tail) ->
                    true %subarray found
                ;
                    findsub([SHead|STail], Tail, XPos) %continue to next el
            )
        ;
            findsub([SHead|STail], Tail, XPos)
    )
.

The program works correctly, but I can't find a way to properly set the value of the Pos variable, so it would return an actual position. Add of Pos is XPos, after parse(STail, Tail) -> , changes the program behaviour to incorrect. I don't understand why. Data for test: findsub("a", "sdfsfgasfdsfsdf", Pos).


Solution

  • Note that in Prolog there is no assignment operation (as in procedural languages). The =/0 operator is used for term unification.

    Looking into your code, we can start by simplifying the parse/2 predicate:

    parse([], _). %parsed sub means that we found it
    parse([SHead|STail], [Head|Tail]) :-
        write(SHead), write(' '), write(STail), write(' | '),
        write(Head), write(' '), write(Tail), nl,
        SHead =:= Head, 
        parse(STail, Tail).
    

    For your findsub/3 predicate, you will need to use an accumulator, i.e. an auxiliary argument to carry the current position, incrementing it when doing a recursive call:

    findsub(SList, List, Position) :-
        findsub(SList, List, 1, Position).
    
    findsub([_|_], [], _, _) :-
        % unparsed sub & empty string means there is no sub
        false.
    findsub([SHead|STail], [Head|Tail], Position0, Position) :-
        (   SHead =:= Head ->
            (   parse(STail, Tail) ->
                % subarray found
                Position = Position0
            ;   % continue to next element
                Position1 is Position0 + 1,
                findsub([SHead|STail], Tail, Position1, Position)
            )
        ;   Position1 is Position0 + 1,
            findsub([SHead|STail], Tail, Position1, Position)
        ).
    

    Your sample call:

    | ?- findsub("a", "sdfsfgasfdsfsdf", Pos).
    
    Pos = 7
    yes