3

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?

tshepang
  • 10,772
  • 21
  • 84
  • 127
Fatalgoddess
  • 119
  • 8
  • Your definition is incorrect: `changeFst(o,[o,o],n,[o,n]).` succeeds, but according to your description it should fail! – false Jun 29 '14 at 22:47
  • Even `changeFst(o,[o,o],n,[]).` succeeds which clearly should fail. – false Jun 29 '14 at 22:50

2 Answers2

3

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],
prolog:dif(Old,_A) ? ;
Olds = [_A,_B,Old|_C],
News = [_A,_B,New|_C],
prolog:dif(Old,_A),
prolog:dif(Old,_B) ? ;
Olds = [_A,_B,_C,Old|_D],
News = [_A,_B,_C,New|_D],
prolog:dif(Old,_A),
prolog:dif(Old,_B),
prolog: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] ? 
yes

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.

Community
  • 1
  • 1
false
  • 10,182
  • 12
  • 93
  • 182
1

Stay pure and efficient, by using if_/3 together with (=)/3 as proposed by @false:

changeFst(Old,Olds,New,News) :-
   list_change_first_(Olds,News,Old,New).

list_change_first_([],[],_,_).
list_change_first_([X|Xs],[Y|Ys],Old,New) :-
   if_(X = Old, (Y = New, Ys = Xs),
                (Y = X,   list_change_first_(Xs,Ys,Old,New))).

Sample queries:

?- changeFst(2,[1,2,3,4,2],10,Xs).
Xs = [1,10,3,4,2].                         % succeeds deterministically

?- changeFst(o,[o,o],n,[m,y,s,t,e,r,y]).
false.                                     % expected result

?- changeFst(Old,Olds,New,News).
Olds = [],             News = []                                       ;
Olds = [Old|_A],       News = [New|_A]                                 ;
Olds = [_A],           News = [_A],           dif(_A,Old)              ;
Olds = [_A,Old|_B],    News = [_A,New|_B],    dif(_A,Old)              ;
Olds = [_A,_B],        News = [_A,_B],        dif(_A,Old), dif(_B,Old) ;
Olds = [_A,_B,Old|_C], News = [_A,_B,New|_C], dif(_A,Old), dif(_B,Old) % and so on...
Community
  • 1
  • 1
repeat
  • 19,449
  • 4
  • 51
  • 152
  • `% and so on...` don't you have a better name for this. Ideally, a [tag:prolog-toplevel] could use such a convention to signal that a query has been stopped. I considered `...` but am not a 100% sure. – false Jun 16 '15 at 09:18
  • @false. How about using the SO keypress markers? I've seen you using them occasionally... Recommendations on how to use them for good? E.g., "Key ;" vs "key enter" vs no key was pressed but an exception raised. – repeat Jun 16 '15 at 09:47
  • It should ideally be valid Prolog syntax, ASCII and printed by the toplevel – false Jun 16 '15 at 09:49