0

Here is my code for one of the root-finding methods.

from __future__ import division
def falseposition(f, a, b, imax=50, tolerance=1.0e-10):
  i = 0
  while i < imax:
    c = (a*f(b)-b*f(a))/(f(b) - f(a))
    if c == 0 or f(c) < tolerance:
      return c, i
    elif (f(a)*f(c))<0:
      b=c
    else:
      a=c
    i += 1
f = lambda x: x**5 - x + 1
root, steps = falseposition(f, 1, 8)
print ("The root is", root, "and the value is", f(root), "; steps taken:", steps)

There is a TypeError. It says that 'NoneType' object is not iterable.

If you see what's the problem, please, tell.

A.Stacey
  • 3
  • 2
  • What happens if the first `if` condition is never satisfied? You should take that into account. – Vasilis G. Nov 17 '17 at 14:13
  • 1
    Its easier for people to help when you post the complete error message you get. – voiDnyx Nov 17 '17 at 14:13
  • If you reach your iteration limit, the function falls off the end, implicitly returning `None`. The error occurs when trying to unpack this `None` into `root` and `steps`. – jasonharper Nov 17 '17 at 14:18

1 Answers1

1

Your falseposition function only explicitely returns when it finds a value of c that meets the criterion before i gets greater than imax. If it doesn't, then it falls out of the while loop, reaches the end of the function body and then (implicitely) returns None. When that's the case, the root, steps = falseposition(f, 1, 8) statement effectively becomes root, steps = None, and since None is not iterable you get this error.

You mainly have two solutions here, either:

return None, i as default at the end of the function (with None signaling the function failed to found a matching value for c):

def falseposition(f, a, b, imax=50, tolerance=1.0e-10):
  i = 0
  while i < imax:
    c = (a*f(b)-b*f(a))/(f(b) - f(a))
    if c == 0 or f(c) < tolerance:
      return c, i
    elif (f(a)*f(c))<0:
      b=c
    else:
      a=c
    i += 1
  # nothing found
  return None, i

or raise some exception instead (and let the caller code catch it):

def falseposition(f, a, b, imax=50, tolerance=1.0e-10):
  i = 0
  while i < imax:
    c = (a*f(b)-b*f(a))/(f(b) - f(a))
    if c == 0 or f(c) < tolerance:
      return c, i
    elif (f(a)*f(c))<0:
      b=c
    else:
      a=c
    i += 1
  # nothing found
  raise ValueError("No matching c value")

Which of those solutions makes sense depends on the context and whatnot, but since obviously there might be quite a few cases where no proper "solution" will be found the first one (returning (None, i)`) seems a good candidate.

You could of course leave the function as is and make sure you test the return value before trying to unpack it:

result = falseposition(f, 1, 8)
if result is None:
   print("oops, nothing found")
root, steps = result
# etc

but that's really really ugly, and bad practice actually (one expects the return value of a function to be of consistant type).

bruno desthuilliers
  • 75,974
  • 6
  • 88
  • 118