views:

355

answers:

2

First off let me state that this is part of a class exercise given as homework. But, the entire assignment is much more involved than the subject of this question. So..

I am searching through two lists given to a predication. My goal is to compare the corresponding elements in this list and determine if the first one is larger. If it is then I need to eventually return a sum of all of those terms. Here is what I have so far:

isumrow([], [], Iresult) :- 

    Iresult is 0.


isumrow([Hi1row | Ti1row], [Hi2row | Ti2row], Iresult) :-   


    if((Hi1row - Hi2row), IsumDiff, Hi1row),   

    NewIresult is IsumDiff + Iresult,

    isumrow(Ti1row, Ti2row, NewIresult),

    Iresult is NewIresult.



if(Diff, Iresult, Entry) :-
    Diff > 0,      

    Iresult is Entry.

if(_, Iresult, _) :-
    Iresult is 0.

For some reason I am messing up somewhere in my assignments and im not sure where. Any hints would be appreciated. Again, this is part of a much larger assignment that I have working but I cannot get this. Thanks

A: 

I'll try to introduce as little changes as possible to your code.

One thing that is definitely wrong is where you try to calculate the difference. Prolog does arithmetic computations only when using is operator. Your code:

if((Hi1row - Hi2row), IsumDiff, Hi1row),

is merely passing the expression of the form (X-Y) to the if predicate, and not calculating it. Later inside if, you do not compute the difference but try to compare the expression to zero... which fails, because you can only compare numbers to numbers, and not to expressions -- and Diff gets assigned to an expression.

It would work if you rewrite the first clause of if as follows (even though you should also get rid of that is here):

if((X-Y), Iresult, Entry) :-
    X > Y,
    Iresult is Entry.

This way your if predicate will get X and Y from the expression to be able to compare them.

Also, you need to avoid your if predicate to yield two possible answers. Your second if clause will be invoked even when X>Y: in the process of backtracking. The easiest way is to put ! at the end of first clause. It means: "Up to this point, I accept the first solution in this program and I don't want to go back from here to find any other solutions". The clause will be changed to:

if((X-Y), Iresult, Entry) :-
    X > Y,
    Iresult is Entry,
    !.

But... this is good in small programs, and if you actually need backtracking in other parts of your program, this can break it. The cleaner way would be to check proper condition in both clauses. Rewrite them to:

if((X-Y), Iresult, Entry) :-
    X > Y,                                           
    Iresult is Entry.

if((X-Y), Iresult, _) :-
    X =< Y,
    Iresult is 0.

Then you're sure that if X>Y, the second clause will fail.

After these modifications your code should work... Please report if it doesn't. It still won't be very prolog-ish though; it is a little bit too verbose.


Edit:

Ok, I'd write it in a simple way:

sum_if_bigger([], [], 0).
sum_if_bigger([A|L1], [B|L2], Result) :-
        sum_if_bigger(L1, L2, Partial),
        Result is Partial + max(0, A-B).

...or in a tail-recursive way:

sum_if_bigger_tr(L1, L2, R) :-
        sum_if_bigger_tr(L1, L2, 0, R).
sum_if_bigger_tr([], [], R, R).
sum_if_bigger_tr([A|L1], [B|L2], Partial, Result) :-
        NewPartial is Partial + max(0, A-B),
        sum_if_bigger_tr(L1, L2, NewPartial, Result).
liori
I understand your revisions and implemented them however I still am not getting the desired results. I am not set on using this code I have written so if there are better or cleaner ways to implement this process, I am open to ideas. Basically, I have 2 lists of lists that I need to compare. As I said earlier, if the corresponding element in List 1 is larger than I will add that element to the running total. That total is what I need for the rest of my program. Again, I am not asking you to solve my problem but I dont think I am grasping the concept of prolog to right way.
Jesse Miller
Could you perhaps provide an alternate way to solve this problem or show some examples? Thanks
Jesse Miller
BTW, you can try to work out how code (both your and mine) work using `trace/0`. Simply enter `trace.` in swipl's prompt, launch a query and press enter to do step-by-step go. Use `notrace.` to stop tracing.
liori
Genius! Works perfectly and I actually understand it! Now this only works on a 1 dimensional list correct. If and wanted to get the entire sum on a 2-dimensional list could I add:total_sum([], [], 0).total_sum([A|L1], [B|L2], Result) :- sum_if_bigger(A, B, RowResult), NewResult is RowResult + Result, total_sum(L1, L2, NewResult), Result is NewResult.Or am I still thinking about this the wrong way?
Jesse Miller
Yes, you do. You firstly assign value to NewResult, then put it to recursive total_sum call. Don't do that. Firstly gather all data: `sum_if_bigger(A, B, SingleRowResult), total_sum(L1, L2, ResultForOtherRows)`, and only then calculate the composite result: `Result is SingleRowResult + ResultForOtherRows`.
liori
awesome, one last question if you are still willing. On each row is the element of List 1 is larger than the corresponding one in List 2 I need to get the sum of all of those elements added to their position in the list (0,1,2,...). The first part is the same but in not sure how to implement the counter portion. Any suggestions? Thanks
Jesse Miller
Well, you have to start with zero, and then with each recursive call increment it. It will probably be easier for you to modify the tail-recursive version: add another parameter to `sum_if_bigger_tr/4`. If you'll have problems doing this, please make another question in SO... this one is getting a bit too long ;-)
liori
A: 
isumrow([], [], Iresult) :-
    Iresult is 0.

isumrow([Hi1row | Ti1row], [Hi2row | Ti2row], Iresult) :-
    if((Hi1row - Hi2row), IsumDiff, Hi1row),              
    NewIresult is IsumDiff + Iresult, 
    isumrow(Ti1row, Ti2row, NewIresult), 
    Iresult is NewIresult. 

if(Diff, Iresult, Entry) :- 
    Diff > 0,                                            
    Iresult is Entry. 

if(_, Iresult, _) :- 
    Iresult is 0. 
bebo