我正在创建一个 RPN 计算器,尝试绘制用户给出的函数。例如,如果用户输入
"x sin 3 * plot"
我希望它绘制
sin(x)*3
其代码如下。注意:问题在
if prompt=="plot"
userInputX="" #userInputX is always replaced before
#running again, so it can be set once here
while True:
print("----------------------------------------------------------")
if not myqueue.isEmpty(): # Display both postfix and infix
print("Postfix: "+str(myqueue))
print("Infix: "+StackCalc.postfix2infix(myqueue))
prompt=input(">")
if prompt == "exp" or prompt == "^": prompt = "**" #<- Easiest way to stop errors with eval(),
if prompt=="quit": break #+ consistent formatting while still accepting exp/^
if prompt=="run":
if myqueue.find("x") is True: # If run, get x value from user, set post2in to the
userInputX=input("Enter x value: ") #result of postfix2infix except replace the x with userinput
post2in=StackCalc.postfix2infix(myqueue).replace("x",userInputX)
print("Solution using infix:",eval(post2in)) #then evaluate it
print("Solution using postfix:",StackCalc.evaluate_postfix(myqueue,userInputX))
continue #skip to next iteration, as "run" shouldnt be pushed to queue
if prompt=="plot":
post2in=StackCalc.postfix2infix(myqueue)
usrInp=input("Enter values of xmin, xmax, nbp: ")#input format "0.5 10.5 1"
usrInp=usrInp.split() # Make it a list,
xmin,xmax,nbp=usrInp[0],usrInp[1],usrInp[2] #set values to input
#this is where it gets questionable
x=np.linspace(float(xmin),float(xmax),int(nbp))
plt.plot(x,post2in) # this is specifically where the issue lies
plt.title("f(x)="+str(post2in))
plt.show()
continue # dont push "plot" to queue
if prompt!="flush":
if prompt == "exp" or prompt == "^": prompt = "**"
myqueue.enqueue(prompt) # same code as optn. 2
else:
myqueue.flush()
要明确的是,
postfix2infix
以字符串形式返回用户的函数。对于我上面使用的示例,它将返回
"sin(x)*3"
作为字符串,我需要将其解释为函数/python 代码。这可能吗?
可以使用
eval()
函数将字符串作为 Python 代码执行,但是
强烈建议不要使用
eval()
处理用户输入,因为这可能会带来安全风险
。如果用户输入恶意代码,
eval()
将会执行它。
更安全的做法是使用
sympy
库解析表达式,然后进行计算和绘图。以下是如何修改代码以使用
sympy
:
import matplotlib.pyplot as plt
import numpy as np
import sympy
# ... 其他代码 ...
if prompt == "plot":
post2in = StackCalc.postfix2infix(myqueue)
usrInp = input("Enter values of xmin, xmax, nbp: ")
usrInp = usrInp.split()
xmin, xmax, nbp = float(usrInp[0]), float(usrInp[1]), int(usrInp[2])
# 使用 sympy 解析表达式
x = sympy.symbols('x')
expr = sympy.sympify(post2in)
# 生成数据用于绘图
x_vals = np.linspace(xmin, xmax, nbp)
y_vals = [expr.subs(x, val).evalf() for val in x_vals]
plt.plot(x_vals, y_vals)
plt.title("f(x) = " + str(expr))
plt.show()
continue
代码解释:
-
导入
sympy
库。 -
将字符串表达式解析为
sympy
表达式。sympy.sympify()
函数将字符串转换为sympy
可以理解的数学表达式。 -
使用
subs()
和evalf()
计算 y 值。-
expr.subs(x, val)
将表达式中的x
替换为x_vals
中的每个值。 -
evalf()
计算替换后的表达式的数值结果。
-
-
使用
matplotlib
绘制图形。
使用示例:
如果用户输入
"x sin 3 * plot"
,代码将会:
-
使用
postfix2infix()
函数将后缀表达式转换为中缀表达式"sin(x)*3"
。 -
使用
sympy.sympify()
将字符串"sin(x)*3"
解析为sympy
表达式。 -
对于
x_vals
中的每个值,使用subs()
将x
替换为该值,然后使用evalf()
计算表达式的值。 -
使用计算出的
x_vals
和y_vals
绘制图形。
安全建议:
即使使用
sympy
,仍然建议对用户输入进行验证,以确保其是有效的数学表达式。可以使用正则表达式或其他方法来验证输入。