HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Cube root (Newton's method)

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
newtonrootcubemethod

Problem

Newton's method for finding cube roots states that for any given \$x\$ and a guess \$y\$, a better approximation is \$\dfrac{(\dfrac{x}{y^2} + 2y)}{3}\$.

What do you think of this code for finding a cube root in Scheme?

(define (improveguess y x)
  ; y is a guess for cuberoot(x)
  (/ (+ (/ x (expt y 2)) (* 2 y)) 3))

(define (cube x) (* x x x))

(define (goodenough? guess x)
  (< (/ (abs (- (cube guess) x)) guess) 0.0001))

(define (cuberoot x) (cuberoot-iter 1.0 x))

(define (cuberoot-iter guess x) 
  (if (goodenough? guess x) guess
      (cuberoot-iter (improveguess guess x) x)))

Solution

If you look at your code for this exercise as well as the one about approximating the square root and the one about finding epsi, you'll notice a common pattern:

You have a function which performs a single step and a predicate which tells you when you're done. You then apply the stepping function until the predicate is met. When you encounter a common pattern like this, the best thing to do is usually to abstract it. So let's define an apply-until function which takes a stepping function, a predicate and an initial value and applies the function to the value until the predicate is met:

(define (apply-until step done? x)
  (if (done? x) x
      (apply-until (step x) step done?)))


You can now define your cuberoot function using apply-until instead of cuberoot-iter:

(define (cuberoot x)
  (apply-until (lambda (y) (improve-guess y x)) (lambda (guess) (goodenough? guess)) 1.0))


You can also move your helper functions as local functions into the cuberoot function. This way they don't need to take x as an argument (as they will close over it) and can thus be passed directly to apply-until without using lambda:

(define (cuberoot x)
  (define (improveguess y)
    ; y is a guess for cuberoot(x)
    (/ (+ (/ x (expt y 2)) (* 2 y)) 3))

  (define (goodenough? guess)
    (< (/ (abs (- (cube guess) x)) guess) 0.0001))

  (apply-until improveguess goodenough? 1.0))

Code Snippets

(define (apply-until step done? x)
  (if (done? x) x
      (apply-until (step x) step done?)))
(define (cuberoot x)
  (apply-until (lambda (y) (improve-guess y x)) (lambda (guess) (goodenough? guess)) 1.0))
(define (cuberoot x)
  (define (improveguess y)
    ; y is a guess for cuberoot(x)
    (/ (+ (/ x (expt y 2)) (* 2 y)) 3))

  (define (goodenough? guess)
    (< (/ (abs (- (cube guess) x)) guess) 0.0001))

  (apply-until improveguess goodenough? 1.0))

Context

StackExchange Code Review Q#1401, answer score: 5

Revisions (0)

No revisions yet.