0

I'm reading SICP and using Dr Racket SICP package as IDE and interpreter. Code from SICP:

(define (sum func a next b)
  (if (> a b)
      0
      (+ (func a)
         (sum func (next a) next b))))
(define (inc n) (+ n 1))

(define (sum-cubes a b)
  (sum cube a inc b))

(define (cube a) (* a a a))

(sum-cubes 1 10)

The function is to calculate the sum of cubic from 1 to 10. However I have no idea what does next b mean, as we are increasing a instead of b, and the increment function is represented by inc.

So I removed the next in next b

Then I got the error: define: duplicate argument identifier in: next

I tried to look up "next" in syntax but found nothing.

Somehow I made this work:

(define (sum func a b)
  (if (> a b)
      0
      (+ (func a)
         (sum func (inc a) b))))
(define (inc n) (+ n 1))

(define (sum-cubes a b)
  (sum cube a b))

(define (cube a) (* a a a))

(sum-cubes 1 10)

But this seems strange. func is replaced by cube, so it's pretty much like a function parameter in C++? But how come it is not (func (inc a))? How come Scheme recognize func (inc a) and knows that it needs to replace it with cube?

Nicholas Humphrey
  • 1,220
  • 1
  • 16
  • 33

2 Answers2

2

next is just a parameter that happens to be a function. It's passed the value inc, so the fragment (next a) in function sum is computing (inc a). The following next b is just passing these values on to the recursive invocation of sum.

Addition

The recursive call just increments the value passed to a and lets b as-is. The recursion stops when a > b. At each level func is applied to a and added to the value being accumulated. func is the function cube in the example. So it's summing cubes as intended.

Gene
  • 46,253
  • 4
  • 58
  • 96
  • Thanks! Could you please look at the edited post? I figured that out one minute ago, but I still don't get it, AFAIK scheme requires all functions to be wrapped by parenthesis, right? So shouldn't it be (sum (func (next a)) next b)))) and we also need to do that for the parameter part? – Nicholas Humphrey May 06 '18 at 01:04
  • Sorry, I think I'm still confused by that "next", why do we need "next b"? Since we are adding a^3 + ... + b^3, b should not change, right? I tried to debug the code and somehow next b is never touched. – Nicholas Humphrey May 06 '18 at 01:07
  • 1
    I'm no scheme expert, but the MIT scheme manual has no standard function or language construct called `func`. – Gene May 06 '18 at 01:09
  • In SICP it's "term", I think it's just a name or somewhat. Anyway the above code does work. – Nicholas Humphrey May 06 '18 at 01:11
  • 1
    Of course it works. Your suggestion that it should be (sum (func (next a)) next b)))) is wrong. – Gene May 06 '18 at 01:13
  • 1
    I read your edited post. Now what I do get is: next is a function, which is inc, this makes sense as it is in the function call. And we don't need (sum (func (next a)) next b)))) because this is just a recursive call to its own, which also makes sense (Jesus I don't know what I was thinking). And next b simply means two things, function next and variable b. Simple. I don't know why I didn't get it earlier, probably because somehow I mistaken inc for cube. Thanks for the help! – Nicholas Humphrey May 06 '18 at 01:18
1

This form:

(+ a b)

Is a combination with 3 variables. +, a, and b are variables and Scheme evaluates them in any order before application, which takes a procedure object and the values.

It's very important to know that arguments in a user define procedure becomes variables. they can hold any value so func and next are just names. In the code you see they are being applied, eg. they get enclosed with parenthesis in operator position, this is the only hint that they are expected to be procedures. That is, besides the documentation in the comments :-)

A procedure in other places than operator position is just a value being passed. Eg. in (sum func (next a) next b) is obvious next is a procedure and that it is used to produce the second argument and that it is passed as a value. It gets bound to next in the next iteration so that it can be used there too. You can actually avoid this by using closures:

;;; sums the numbers a ... b
;;; using procedure func as key and
;;; procedure next to compute next value
;;; example:
;;; (sum (λ (v) (* v 2)) ; func
;;;      1               ; a
;;;      (λ (v) (+ v 2)) ; next
;;;      10)             ; b 
;;; => 50
;;; = (+ 2 6 10 14 18)
(define (sum func a next b)
  (define (helper a)
    (if (> a b)
        0
        (+ (func a)
           (helper (next a)))))
  (helper a))

A more clear way than the above is to use named let and you can also introduce an accumulator that makes this tail recursive, but I guess you will learn this eventually just following the book.

Sylwester
  • 47,942
  • 4
  • 47
  • 79