Reputation: 1
I've made a predicate that takes 2 list as arguments and returns a list concatenated with the product of "recipesub", however I need to make a third rule that prohibits the product from being inserted if the product at hand is an empty list.
So the first list could look like this:
recipe([ingredient(rice,4),ingredient(salt,3),ingredient(water,5)]).
And the second one like this:
ingredients([ingredient(rice,3),ingredient(salt,4),ingredient(water,4),
At the moment it returns: List = [ingredient(rice,1),[],ingredient(water,1)]
I WANT it to return: List = [ingredient(rice,1),ingredient(water,1)]
need_to_buy([],_,List):- List = [].
need_to_buy([H|Hs],[X|Xs],[Difference|List]):-
H = ingredient(Type,Amount),
recipesub(Type,Amount,[X|Xs],Difference),
need_to_buy(Hs,[X|Xs],List).
Below is how far I've gotten with the solution.
/*need_to_buy([H|Hs],[X|Xs],List):-
H = ingredient(Type,Amount),
recipesub(Type,Amount,[X|Xs],Difference),
Difference = [],
need_to_buy(Hs,[X|Xs],List).*/
And this is the support-predicate, recipesub.
recipesub(Type,Amount,[],Difference):-
Difference = ingredient(Type,Amount).
recipesub(Type,Amount,[Z|_],Difference):-
Z = ingredient(Type,Stock),
Amount>Stock,
NewAmount is Amount-Stock,
Difference = ingredient(Type,NewAmount).
recipesub(Type,Amount,[Z|_],Difference):-
Z = ingredient(Type, Stock),
Stock >= Amount,
Difference = [].
recipesub(Type,Amount,[Z|Zs],Difference):-
Z = ingredient(WrongType,_),
WrongType \= Type,
recipesub(Type,Amount,Zs,Difference).
Upvotes: 0
Views: 89
Reputation: 1
I ultimately solved it by splitting the second rule of need_to_buy into two rules, one which handles the case where difference is an empty list, and on where it isnt an empty list.
I had some trouble with it at first but it turns out the "orientation" of the rules was giving me trouble, so I had to place the rule which handles the case where Difference \= [], above the one where Difference = []. It now looks like this:
need_to_buy([],_,List):- List = [].
need_to_buy([H|Hs],[X|Xs],[Difference|List]):-
H = ingredient(Type,Amount),
recipesub(Type,Amount,[X|Xs],Difference),
Difference \= [],
need_to_buy(Hs,[X|Xs],List).
need_to_buy([H|Hs],[X|Xs],List):-
H = ingredient(Type,Amount),
recipesub(Type,Amount,[X|Xs],Difference),
Difference = [],
need_to_buy(Hs,[X|Xs],List).
Upvotes: 0
Reputation: 22803
I normally don't do a bunch of nested conditionals but it "felt right" this time, and this is the solution I found:
need_to_buy([], _, []).
need_to_buy([ingredient(Type, AmountNeeded)|Ingredients], OnHand, Needed) :-
% Do we have any on-hand?
member(ingredient(Type, AmountOnHand), OnHand) ->
% If the amount on-hand is greater than the amount needed,
% just hand off the rest
(AmountOnHand >= AmountNeeded ->
need_to_buy(Ingredients, OnHand, Needed)
% otherwise, calculate the amount needed and recur
; (AmountToBuy is AmountNeeded - AmountOnHand,
need_to_buy(Ingredients, OnHand, RestNeeded),
Needed = [ingredient(Type, AmountToBuy)|RestNeeded]))
% If we have none on-hand, we can just use the amount needed
% to form the request, and recur
; need_to_buy(Ingredients, OnHand, RestNeeded),
Needed = [ingredient(Type, AmountNeeded)|RestNeeded].
Otherwise I think you'll have a lot of fairly inefficient testing and retesting. The main mistake I see in your code is that you're pattern matching on the second argument. It's easier to rely on member/2
or memberchk/2
to do the dirty work of finding the right ingredient in the stuff you have on-hand.
If I did it with a bunch of clauses instead it would probably look like this:
need_to_buy([], _, []).
% case 1: we don't have the ingredient at all
need_to_buy([ingredient(Type, AmountNeeded) | Ingredients],
OnHand,
[ingredient(Type, AmountNeeded)|Needed]) :-
\+ memberchk(ingredient(Type, _), OnHand),
need_to_buy(Ingredients, OnHand, Needed).
% case 2: we have it, but not enough
need_to_buy([ingredient(Type, AmountNeeded) | Ingredients],
OnHand,
[ingredient(Type, AmountToBuy)|RestNeeded]) :-
memberchk(ingredient(Type, AmountOnHand), OnHand),
AmountNeeded > AmountOnHand,
AmountToBuy is AmountNeeded - AmountOnHand,
need_to_buy(Ingredients, OnHand, RestNeeded).
% case 3: we have enough
need_to_buy([ingredient(Type, AmountNeeded) | Ingredients],
OnHand,
RestNeeded) :-
memberchk(ingredient(Type, AmountOnHand), OnHand),
AmountNeeded =< AmountOnHand,
need_to_buy(Ingredients, OnHand, RestNeeded).
This leaves a choice point on the stack and just generally seems like a lot of retesting the same conditions and re-traversal for my taste. But if it looks better to you it should work the same.
Upvotes: 1