# simple math expression parser
def lexer(s):
'''token generator, yields a list of tokens'''
yield s
if __name__ == '__main__':
for token in lexer("1 + (2 - 3) * 456"):
print token
$ ipython
IPython 0.13.1 -- An enhanced Interactive Python.
? -> Introduction and overview of IPython's features.
...
In [1]: run expr.py
1 + (2 - 3) * 456
在 IPython 里面用 run 跑的好处有很多,首先是你在程序执行完毕后整个程序的状态,比如最后全局变量的值,你写的函数这些你都是可以随便执行的!同样的你可以在 IPython 里面保存一些用来测试的常量,每次用 run 跑的话新的程序会被重新载入,你可以这样方便的测试每个函数,有一个非常动态的环境来调试你的程序:
In [4]: s = "foo" # 忘记判断字符串是数字的函数的名字了,用一个字符串试试看
In [5]: s.is # 开头大概是 is,这里按下 tab 键 IPython 会帮我们补全
s.isalnum s.isalpha s.isdigit s.islower s.isspace s.istitle
In [6]: s.isdigit? # 结果是 isdigit,在表达式后加上问号并回车查看文档
Type: builtin_function_or_method
String Form:
Docstring:
S.isdigit() -> bool
Return True if all characters in S are digits
and there is at least one character in S, False otherwise.
In [8]: s.isdigit() # 调用试试看
Out[8]: False
In [9]: 'f' in 'foo' # 试试字符串能不能用 in 来判断
Out[9]: True
# simple math expression parser (broken lexer)
def lexer(s):
'''token generator'''
ix = 0
while ix < len(s):
if s[ix].isspace(): ix += 1
if s[ix] in "+-*/()":
yield s[ix]; ix += 1
if s[ix].isdigit():
jx = ix + 1
while jx < len(s) and s[jx].isdigit(): jx += 1
yield s[ix:jx]; ix = jx
else:
raise Exception("invalid char at %d: '%s'" % (ix, s[ix]))
yield ""
if __name__ == '__main__':
print list(lexer("1 + (2 - 3) * 456"))
看起来不错,我们还是在 IPython 里执行试试,结果发现程序抛出了一个异常:
In [6]: run expr.py
------------------------------------------------------------------
Exception Traceback (most recent call last)
py/expr.py in ()
18
19 if __name__ == '__main__':
---> 20 print list(lexer("1 + (2 - 3) * 456"))
py/expr.py in lexer(s)
13 yield s[ix:jx]; ix = jx
14 else:
---> 15 raise Exception("invalid character at ...))
16 yield ""
17
Exception: invalid character at 3: ' '
嗯?好像程序里已经处理了空格的情况。怎么会这样?不知道你碰到异常的时候一般都怎么办。你可能会选择到处添加 print,用 IDE 断点调试。其实这种情况用 pdb 是很明智的选择,在 IPython 里我们可以非常轻松的使用它。
In [13]: pdb # 开启 pdb ,这样在异常的时候我们会自动的 break 到异常处
Automatic pdb calling has been turned ON
In [14]: run expr.py
-----------------------------------------------------------------
Exception: invalid character at 3: ' '
> py/expr.py(15)lexer()
14 else:
---> 15 raise Exception("invalid char at ...))
16 yield ""
ipdb> print ix # 这里我们可以执行任何 Python 的代码
3
ipdb> whatis ix # 也可以用 pdb 提供的命令,输入 help 可以查看所有命令
通过方便的调试和仔细检查代码,我们发现是没有正确的使用 elif 造成了问题!(我知道这个过程不是太符合情理...)。把代码里的后面的几个 if 都换成 elif 以后我们发现结果基本上是对的了。我们可以马上再跑几个类似的例子,确认不同的输入是否都有比较好的结果:
In [18]: run expr.py # 这次差不多对了,我们可以试试几个别的例子
['1', '+', '(', '2', '-', '3', ')', '*', '456', '']
In [19]: print list(lexer("1*123*87-2*5"))
['1', '*', '123', '*', '87', '-', '2', '*', '5', '']
# 跟在 shell 里面一样,你可以用上下来选取之前的记录,然后简单的修改再重新执行。
# 记得每次 run 后你的函数都是最新版本,你可以很简单的用重复的数据来测试你的函数
# IPython 甚至还实现了 Ctrl+R!自己试试看吧
In [19]: print list(lexer("1 + two"))
Exception: invalid character at 2: 't'...