Hello everyone, my latest project is this:
1) Ask user to input a function
2) Plot said function
Well, I figured out how to strip the function into two parts, input a number, and evaluate the function. That all works fine outside of a defined function I created. The trouble I have, is now when asking for user input, it screws it up. I can't really explain it, but it looks like the results are just repeats of a few numbers. Here's my code:

from numpy import *
from pylab import *

numpyfunctions = [
    'abs',
    'absolute',
    'angle',
    'sqrt',
    'log',
    'sin',
    'sinh',
    'cos',
    'cosh',
    'tan',
    'tanh']

def frange(start, end=None, inc=None):
    "A range function, that does accept float increments..."

    if end == None:
        end = start + 0.0
        start = 0.0

    if inc == None:
        inc = 1.0

    L = []
    while 1:
        next = start + len(L) * inc
        if inc > 0 and next >= end:
            break
        elif inc < 0 and next <= end:
            break
        L.append(next)
        
    return L

def funeval(function, numbers=range(-5, 5), splitat='x'):
    '''Takes the function (string) and runs through a list of numbers,
       then return a list of numbers that equals the function'''
    # List of the function's results
    result = []
    for number in numbers:
        # Usually not a good idea to divide anything by zero :D
        if number != 0:
            # Split the function input
    	    # Usually at 'x'
            # Then, we put together the number
            # inside the function in replace
            # of the 'x'
            b = function.split(splitat)
            c = ''
            for y in b:
            	c += y
            	if y != b[-1]:
            		c+= '%d' %(number)
            result.append(eval(c))
        if number == 0:
           result.append(0)
    return result

def menu():
    print("Function Plotter v1.0")
    print("~Hondros")
    print("Type in a function, or type 'help' for function commands")
    print("Type frange(x, y) for a specific x range)")
    print("example: 2*sin(x)")
    print("")
    #ran = frange(-5, 5, 0.01)
    while True:
        try:
            f = raw_input("y = ")
            if f == 'help':
                menuhelp()
            if f[0:6] == 'frange':
                ran = eval(f)
            else:
                y = funeval(f, ran)
                #plot(ran, y)
                #show()
        except:
            print("The function %s is invalid" %(f))

menu()

Any thoughts? If needed, please look at attached .txt file. That's the output I have when I first tried creating this.

Dani AI

Generated

The immediate cause of the symptom where only the "else" branch ran was the control-flow structure: several independent if statements with a single else attached to the last one. In Python an else pairs with the nearest preceding if, so commands handled by earlier ifs still fall through and trigger the final else. 's suggestion to use elif (or to continue/return after handling a command) is the right fix.

A few other things to harden and speed the plotter that are not explicit in the thread:

  • Do not modify the list you are iterating over (popping items from numlist inside for x in numlist). That leads to skipped elements and subtle bugs. Build a new list of accepted x/y pairs or iterate over a copy instead.
  • Avoid bare except: blocks. They hide the real error and make debugging painful. Catch specific exceptions (for example, ZeroDivisionError, NameError, SyntaxError) and print or log the exception message/traceback while you are debugging.
  • eval per-sample is slow and unsafe. For performance and safety evaluate expressions against a controlled namespace (only expose necessary functions) or use a single compiled callable (or a vectorized numpy expression) rather than calling eval for each x. Consider libraries or ast-based parsing if untrusted input must be allowed.
  • Be careful with recursion in the menu (calling menu(...) from inside menu). Repeated calls grow the call stack; prefer updating the loop state and continuing.

Quick troubleshooting checklist: test with a tiny range first, print the substituted expression before eval to confirm what you evaluate, enable full tracebacks during development, and replace broad excepts with specific handlers. The final code posted by works, but following the above will make it more robust, faster, and safer.

Recommended Answers

All 3 Replies

Okay, I redid the code, and it's all working now. Don't know what the issue was, but this version works a LOT better. However, I have a new issue. This time, it won't do anything except the "else" clause. Any ideas?

from numpy import *
from pylab import *

def frange(start, end=None, inc=None):
    "A range function, that does accept float increments..."

    if end == None:
        end = start + 0.0
        start = 0.0

    if inc == None:
        inc = 1.0

    L = []
    while 1:
        next = start + len(L) * inc
        if inc > 0 and next >= end:
            break
        elif inc < 0 and next <= end:
            break
        L.append(next)
        
    return L

def funceval(function, numlist=frange(-100, 100, 0.01), splitat='x'):
    y = []
    for x in numlist:
        try:
            c = '%s%s%s'%(function.split(splitat)[0], str(x), function.split(splitat)[1])
            y.append(eval(c))
        except:
            numlist.pop(numlist.index(x))
    return y

def ranradians(ran):
    a = []
    for x in ran:
        a.append(radians(x))
    return a

def menu():
    print("Function Plotter v2.0")
    print("~Hondros")
    print("Type in a function, or type 'help' for function commands")
    print("Type frange(x, y) for a specific x range)")
    print("Type set(radian) to convert list of numbers to radians (for sin)")
    print("Type show to show the plot window")
    print("example: 2*sin(x)")
    print("")
    while True:
        try:
            ran = frange(-100, 100, 0.01)
            f = raw_input("y = ")
            if f == 'help':
                menuhelp()
            if f[0:6] == 'frange':
                ran = eval(f)
            if f == 'set(radian)':
                ran = ranradians(ran)
            if f == 'show':
                show()
            else:
                y = funceval(f, ran)
                plot(ran, y)
        except:
            print("The function %s is invalid" %(f))

#menu()

I don't have numpy, but try replacing all of the other 'ifs' with 'elif' and see if that works.

Okay, fixed it. Again, don't know what the issue is. Anyways, if anyone wants the finished code, here it is:
(note that numpy and pylab are required)

from numpy import *
from pylab import *

def frange(start, end=None, inc=None):
    "A range function, that does accept float increments..."

    if end == None:
        end = start + 0.0
        start = 0.0

    if inc == None:
        inc = 1.0

    L = []
    while 1:
        next = start + len(L) * inc
        if inc > 0 and next >= end:
            break
        elif inc < 0 and next <= end:
            break
        L.append(next)
        
    return L

def funceval(function, numlist=frange(-100, 100, 0.01), splitat='x'):
    y = []
    for x in numlist:
        try:
            numfunction = ''
            for c in function.split(splitat)[:-1]:
                numfunction += c
                numfunction += '(%f)' %(x)
            numfunction += function.split(splitat)[-1]
            y.append(eval(numfunction))
        except:
            numlist.pop(numlist.index(x))
    return y

def ranradians(ran):
    a = []
    for x in ran:
        a.append(radians(x))
    return a

def menuhelp():
    print("Function Plotter v2.0")
    print("Help")
    print("")
    print("Functions can be in any form, so long as there is an 'x'")
    print("Standard form for parabola's typing in the function is:")
    print("A*(x**2)+B*x+C")
    print("Carry this form to any function")
    print("Make sure to type in every symbol, none are implied")
    print("Pi is: pi")
    print("Using sine or similar functions: sin(x)")
    print("""Functions usable are:
'abs'
'absolute'
'angle'
'sqrt'
'log'
'sin'
'sinh'
'cos'
'cosh'
'tan'
'tanh'
""")
    print("For further help, please look at the source code to figure it out,")
    print("Or, e-mail me at: alexpolosky@gmail.com")

def menu(ran=frange(-100, 100, 0.01), once=False):
    if once != True:
        print("Function Plotter v2.0")
        print("~Hondros")
        print("Type in a function, or type 'help' for function commands")
        print("Type frange(x, y) for a specific x range)")
        print("Type set(radian) to convert list of numbers to radians (for sin)")
        print("Type show() to show the plot window")
        print("example: 2*sin(x)")
        print("")
    while True:
        try:
            f = raw_input("y = ")
            if f == 'help':
                menuhelp()
            if f[0:6] == 'frange':
                menu(eval(f), True)
            if f == 'set(radian)':
                menu(ranradians(ran), True)
            if f == 'show':
                show()
            else:
                y = funceval(f, ran)
                plot(ran, y)
        except:
            print("The function %s is invalid" %(f))

menu()
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.