Search code examples
listprolog

Prolog - replacing list elements


So I have a program that has a predicate to replace the first occurrence of some element of a list with a given new element and produce a new list. I have it done like so:

    changeFst(OldE,[OldE|T],NewE,[NewE|T]):-!.
    changeFst(OldE,[_|T],NewE,_):- changeFst(OldE,T,NewE,_),!.

For example if you give (2,[1,2,3,4,2],10,X) it should give you back X=[1,10,3,4,2]

now im making the part that changes the last occurrence (in the example it would return X=[1,2,3,4,10]). Here's my code:

    changeLast(OldE,OldL,NewE,NewL):-
       reverse(OldE,X), 
       changeFst(OldE,X,NewE,NewL),
       !.

so this works in fact perfectly but the thing is it returns me the list reversed ( in my upper example it returns me [10,4,3,2,1] instead of [1,2,3,4,10])

How can I just reverse this again to get my answer properly displayed?


Solution

  • Your definition of changeFst/4 is incorrect on many counts like changeFst(o,[o,o],n,[m,y,s,t,e,r,y]). succeeds, but clearly it should fail. The reason is your incorrect usage of cuts. If you want to learn Prolog, stick to the pure declarative subset first. That means no cuts, and no side-effects.

    So here is a definition that does not rely on cuts:

    changeFst(Old,[Old|Olds],New,[New|Olds]).
    changeFst(Old,[E|Olds],New,[E|News]):-
       dif(Old, E),
       changeFst(Old,Olds,New,News).
    

    One advantage of such a pure relation is that we can use the most general query to see what answers we get:

    ?- changeFst(Old, Olds, New, News).
       Olds = [Old|_A], News = [New|_A]
    ;  Olds = [_A,Old|_B], News = [_A,New|_B], dif(Old,_A)
    ;  Olds = [_A,_B,Old|_C], News = [_A,_B,New|_C],
       dif(Old,_A), dif(Old,_B)
    ;  Olds = [_A,_B,_C,Old|_D], News = [_A,_B,_C,New|_D],
       dif(Old,_A), dif(Old,_B), dif(Old,_C)
    ; ... .
    

    Do you note that the answers always contain for Olds a partial list? Like: Olds = [Old|_A] in the first answer. That might be a bit too general, after all it means that even nonlists are now accepted:

    ?- changeFst(o,[o|nonlist], New, News).
       News = [New|nonlist]
    ;  false.
    

    So you might want to ensure that Olds and News are always lists.

    But my point to show this is rather to you show you that with a pure relation you see many things that a cut-ridden program can never show you that directly.

    If we are at it: What to do with the empty list? The current version suggests that changeFst/4 should fail. Not sure what you want here, but in case you want it to succeed, add a fact changeFst(_,[],_,[]). first.

    See this answer for a definition of dif/2 () should your Prolog not support it.