2
votes

Hiyya,

I'm currently working on a type of "virtual shop" in Prolog, and I'm trying to work on the functionality to update an aisle. However, seeing my rather limited knowledge of Prolog, I'm facing some difficulties.

The predicate(s) and call:

/*  % First parameter: The shop (or "old list") that contains the unmodified data
    % Second parameter: Simply a index/counter keeping track of where we are in 
        the "recursive loop"
    % Third parameter: Index of where we want to get. We keep recursively looping 
        and either incrementing or decrementing until the second and third 
        parameters are the same.
    % Fourth parameter: Since the first parameter is a list of lists, this 
        parameter will contain the "inner list" which will replace another inner 
        list.
    % Fifth parameter: The shop (or "new list"), the result, whatever you'd like 
        to call it 
    */
update_aisle( [H|T], CurrentAisle, TargetAisle, NewObj, NewShop):-
    CurrentAisle < TargetAisle,
    Temp is CurrentAisle + 1,
    append( NewShop, H),
    update_aisle( T, Temp, TargetAisle, NewObj, NewShop).
update_aisle( [H|T], CurrentAisle, TargetAisle, NewObj, NewShop):-
    CurrentAisle > TargetAisle,
    Temp is CurrentAisle - 1,
    append( NewShop, H),
    update_aisle( T, Temp, TargetAisle, NewObj, NewShop).
update_aisle( [H|T], CurrentAisle, TargetAisle, NewObj, NewShop):-
    CurrentAisle is TargetAisle,
    nth0( 0, T, TempT),
    append( NewObj, TempT, NewShop).

?- Shop = [ [ "Bread", "Donuts", "Cookies" ], 
            [ "Beer", "Cider", "Juice" ],
            [ "Ham", "Raw Meat", "Sausage" ] ],
   write(Shop), nl,
   update_aisle(Shop, 0, 1, ["Beer", "Milk", "Juice"], NewShop),
   write(NewShop), nl.

Right now, I'm only getting directive (failed), so not getting very far at all (and I'm going to assume that's because append/2 is not cooperating as I'd want it to). However, what I'd want to achieve is this:

Shop = [ [ "Bread", "Donuts", "Cookies" ], 
         [ "Beer", "Cider", "Juice" ],
         [ "Ham", "Raw Meat", "Sausage" ] ].

NewShop = [ [ "Bread", "Donuts", "Cookies" ], 
            [ "Beer", "Milk", "Juice" ], 
            [ "Ham", "Raw Meat", "Sausage" ] ].

So in other words, I'm trying to loop through the list, and append the "head" of the list for each recursive loop for as long as we're not where we want to be in the list. Once we've reached that point, we're mostly out after appending NewObj to the list (instead of the head, as that is the one being replaced) and job done.

I feel like I'm missing something glaringly obvious, but any and all help would be appreciated!

1
append/2 is definitely not correclty used. Wherever it appears, it will make H the result of merging the sublists of NewShop. That's not what is wanted. Also, ` CurrentAisle is TargetAisle` is wrong. [is/2] is arithmetic evaluation of the right-hand-side. You want to check for equality, right? Then = ("make sure left and right unify") or =:= (make sure arithmetic expressions on the left and right rfesolve the same) - David Tonhofer
@DavidTonhofer Absolutely not trying to say you'd be wrong, but I just feel like that contradicts their documentation, that says append/2 is append(+ListOfLists, ?List)? Unless their documentation does not keep orders in mind? - Xariez
Try on the REPL: forwards: append( [[1,2],[3,4],[5,6]] ,X). gives X = [1, 2, 3, 4, 5, 6]. backwards: bagof([A,B], append( [A,B] , [1,2] ), S). gives S = [[[], [1, 2]], [[1], [2]], [[1, 2], []]]. - David Tonhofer
@DavidTonhofer Hm, okay! Does not seem to have resolved the issue simply by giving the arguments in the right order though, unfortunately. - Xariez
Could you explain in simple words what update_aisle is supposed to do? What's its purpose? It's not clear to me why it needs all of those arguments. I know what each one means individually based upon their names, but why you need them or want them is unclear. - lurker

1 Answers

1
votes

If I'm interpreting your question correctly, you want to replace an item at a given index, in a list:

% replace_at( In, At, With, Replaced )
replace_at( [],  _I, _N, []).
replace_at( [_|T], I, N, [N|T]) :- I =:= 0.
replace_at( [H|T], I, N, [H|T2]) :- I > 0, I2 is I-1,
   replace_at( T, I2, N,    T2).

Testing:

?- Shop = [["Bread","Donuts","Cookies"],["Beer","Cider","Juice"],["Ham","Raw Meat","Sausage"]], 
   replace_at( Shop, 1,    ["Beer", "Milk", "Juice"],    NewShop), nl, 
   maplist( writeln, Shop), nl, maplist( writeln, NewShop), nl.

[Bread,Donuts,Cookies] 
[Beer,Cider,Juice]
[Ham,Raw Meat,Sausage]

[Bread,Donuts,Cookies]
[Beer,Milk,Juice]
[Ham,Raw Meat,Sausage]

Shop = [["Bread", "Donuts", "Cookies"], ["Beer", "Cider", "Juice"], ["Ham", "Raw Meat", "Sausage"]],
NewShop = [["Bread", "Donuts", "Cookies"], ["Beer", "Milk", "Juice"], ["Ham", "Raw Meat", "Sausage"]] ;
false.

Gives the output that you wanted.

That the elements of the list are themselves lists is immaterial here.

If on the other hand you shall ever want two indices to be used in the call, to specify that the Jth element in the Ith list (0-based) in a list of lists be replaced, you can just use the above predicate, twice, to accomplish that:

% replace_at2d( In, AtLine, AtWord, With, Replaced )
replace_at2d(  LL,    I, J, W,     LL2 ) :-
  nth0(   I,   LL, L),                         % I-th line
  replace_at(      L,    J, W, L2 ),           % J-th word
  replace_at(  LL,    I,       L2, LL2 ).

Testing:

?- Shop = [["Bread","Donuts","Cookies"],["Beer","Cider","Juice"],["Ham","Raw Meat","Sausage"]], 
   replace_at2d( Shop, 1, 1,    "Milk",    NewShop), nl, 
   maplist( writeln, Shop), nl, maplist( writeln, NewShop), nl.

[Bread,Donuts,Cookies]
[Beer,Cider,Juice]
[Ham,Raw Meat,Sausage]

[Bread,Donuts,Cookies]
[Beer,Milk,Juice]
[Ham,Raw Meat,Sausage]

Shop = [["Bread", "Donuts", "Cookies"], ["Beer", "Cider", "Juice"], ["Ham", "Raw Meat", "Sausage"]],
NewShop = [["Bread", "Donuts", "Cookies"], ["Beer", "Milk", "Juice"], ["Ham", "Raw Meat", "Sausage"]] ;
false.