Search code examples
schemesicp

Add successive integers with recursive tests (conditionals)


My recursive function correctly sums all the integers, inclusively, between a and b. However, when I swap my arguments, the result is incorrect. I am reading SICP. My code is (scheme):

 (define (sum-integers a b)
   (if (> a b)
      (if (= (- a 1) b)
       (+ a b)
        (+ a(sum-integers (- a 1) b)))
     (+ a (sum-integers (+ a 1) b))))

     (sum-integers 5 2)
     (sum-integers 2 5)

I'm sure my compiler is applicative order,so I think the output should be the same.

      (sum-integers 5 2)
      (sum-integers 2 5)

In fact,

  (sum-integers 5 2) ==> 14
  (sum-integers 2 5) ==> 25

But 2+3+4+5 = 5+4+3+2 = 14.

Why are my results different?


Solution

  • In your second test case, which displays the bug, your loop doesn't terminate until a = 6 and b = 5. Then it adds an extra 6 and 5 to the total of 14, to get 25.

    The reason you found this bug tricky to find may be because the code was more complex than it could be, or because you haven't yet learned how to trace recursive code.

    I also noticed that your code indentation is not consistent. Let your editor indent your code for you, to help you follow the logic.

    I have written up the code for the design @alfasin mentioned in the comments. You can see the simplicity makes it easier to debug. If a > b then we just swap the arguments and carry on as before.

    I think the key to thinking about this is to make sure that the operation that you want to repeat (here it is addition) appears only once in your function.

    (define (sum2-integers a b)
      (if (> a b)
        (sum2-integers b a)   ; if the wrong way round, swap them
        (if (= a b)           ; termination condition
           b
           (+ a (sum2-integers (+ a 1) b)))))
    
    > (sum2-integers 5 2)
    14
    > (sum2-integers 2 5)
    14
    

    One step further towards simplicity. I find that the multi-way conditional branch cond is much more readable than nested ifs, as there are really three branches in this code.

    (define (sum3-integers a b)
      (cond [(> a b) (sum3-integers b a)]
            [(= a b) b]
            [else (+ a (sum3-integers (+ a 1) b))]))