1

I am creating a sigsum() function which takes the sum using an input equation and an input variable. Here's what I have so far:

def sigsum(eqn, index, lower=0, upper=None, step=1):

    if type(step) is not int:
        raise TypeError('step must be an integer')
    elif step < 1:
        raise ValueError('step must be greater than or equal to 1')

    if upper is None:
        upper = 1280000

    if lower is None:
        lower = -1280000

    if (upper - lower) % step:
        upper -= (upper - lower) % step

    index = lower
    total = 0
    while True:
        total += eqn
        if index == upper:
            break
        index += step

    return total

Usage of function:

print(sigsum('1/(i+5)','i'))
>>> 12.5563

My current problem is converting 'eqn' and 'index' to variables that exist inside the function local namespace. I heard around that using exec is not a good idea and that maybe setattr() might work. Can anyone help me out? Thanks.

Brandon H. Gomes
  • 808
  • 2
  • 9
  • 24

2 Answers2

2

For eqn I suggest using a lambda function:

eqn = lambda i: 1 / (i + 5)

then index is not needed, because it is just "the variable passed to the function" (does not need a name).

Then your function becomes

def integrate(fn, start = 0, end = 128000, step = 1):
    """
    Return a stepwise approximation of
      the integral of fn from start to end
    """
    num_steps = (end - start) // step
    if num_steps < 0:
        raise ValueError("bad step value")
    else:
        return sum(fn(start + k*step) for k in range(num_steps))

and you can run it like

res = step_integrate(eqn)   # => 10.253703030104417

Note that there are many steps to this, and many of them involve very small numbers; rounding errors can become a major problem. If accuracy is important you may want to manually derive an integral,

from math import log

eqn          = lambda i: 1 / (i + 5)
eqn.integral = lambda i: log(i + 5)

def integrate(fn, start = 0, end = 128000, step = 1):
    """
    Return the integral of fn from start to end

    If fn.integral is defined, used it;
    otherwise do a stepwise approximation
    """
    if hasattr(fn, "integral"):
        return fn.integral(end) - fn.integral(start)
    else:
        num_steps = (end - start) // step
        if num_steps < 0:
            raise ValueError("bad step value")
        else:
            return sum(fn(start + k*step) for k in range(num_steps))

which again runs like

res = step_integrate(eqn)   # => 10.150386692204735

(note that the stepwise approximation was about 1% too high.)

Hugh Bothwell
  • 55,315
  • 8
  • 84
  • 99
1

I would use a lambda function as Hugh Bothwell suggested you would have to modify sigsum as the following

def sigsum(eqn, lower=0, upper=None, step=1):

    if type(step) is not int:
        raise TypeError('step must be an integer')
    elif step < 1:
        raise ValueError('step must be greater than or equal to 1')

    if upper is None:
        upper = 1280000

    if lower is None:
        lower = -1280000

    if (upper - lower) % step:
        upper -= (upper - lower) % step

    index = lower
    total = 0
    while True:
        total += eqn(index)
        if index == upper:
            break
        index += step

    return total

Usage of function:

print(sigsum(lambda i: 1/(i+5)))
>>> 12.5563

you can also define a function separatly:

def myfunction(i):
     return 1/(i+5)

and pass it to sigsum

print(sigsum(myfunction))
>>> 12.5563

to be able to pass function as a parameter is called in computer language speech function as first class object. (C and java for example doesn't have it, javascript and python have)

Xavier Combelle
  • 10,968
  • 5
  • 28
  • 52
  • Thanks. Awesome help guys. I guess I didn't need setattr(). For 'eqn': if I input any function that I define elsewhere will it work? – Brandon H. Gomes Jan 31 '15 at 02:48