(点击上方公号,可快速关注)

作者:Stories For Sad Robots

译者:开源中国社区

链接:http://www.oschina.net/translate/how-to-write-a-calculator-in-50-python-lines-without-eval

简介

在这篇文章中,我将向大家演示怎样向一个通用计算器一样解析并计算一个四则运算表达式。当我们结束的时候,我们将得到一个可以处理诸如 1 2*-(-3 2)/5.6 3样式的表达式的计算器了。当然,你也可以将它拓展的更为强大。

我本意是想提供一个简单有趣的课程来讲解 语法分析 和 正规语法(编译原理内容)。同时,介绍一下PlyPlus,这是一个我断断续续改进了好几年的语法解析 接口。作为这个课程的附加产物,我们最后会得到完全可替代eval()的一个安全的四则运算器。

如果你想在自家的电脑上试试本文中给的例子的话,你应该先安装 PlyPlus ,使用命令pip install plyplus 。(译者注:pip是一个包管理系统,用来安装用python写的软件包,具体使用方法大家可以百度之或是google之,就不赘述了。)

本篇文章需要对python的继承使用有所了解。

语法

对于那些不懂的如何解析和正式语法工作的人而言,这里有一个快速的概览:正式语法是用来解析文本的一些不同层面的规则。每一个规则都描述了相对应的那部分输入的文本是如何组成的。

这里是一个用来展示如何解析1 2 3 4的例子:

Rule#1 - add IS MADE OF add number

ORnumbernumber

或者用 EBNF:

add:add' 'number

|number' 'number

;

解析器每次都会寻找add number或者number number,找到一个之后就会将其转换成add。基本上而言,每一个解析器的目标都在于尽可能的找到最高层次的表达式抽象。

以下是解析器的每个步骤:

number number number number

第一次转换将所有的Number变成“number”规则

[number number] number number

解析器找到了它的第一个匹配模式!

[add number] number

在转换成一个模式之后,它开始寻找下一个

[add number]

add

这些有次序的符号变成了一个层次上的两个简单规则: number number和add number。这样,只需要告诉计算机如果解决这两个问题,它就能解析整个表达式。事实上,无论多长的加法序列,它都能解决! 这就是形式文法的力量。

运算符优先级

算数表达式并不仅仅是符号的线性增长,运算符创造了一个隐式的层次结构,这非常适合用形式文法来表示:

1 2 * 3 / 4 5 6

这相当于:

1 (2 * 3 / 4) 5 6

我们可以通过嵌套规则表示此语法中的结构:

add:addmul

|mul' 'mul

;

mul:mul'*; number

| number'*'number

;

通过将add设为操作mul而不是number,我们就得到了乘法优先的规则。

让我们在脑海中模拟一下使用这个神奇的解析器来分析1 2*3*4的过程:

number number * number * number

number [number * number] * number

解析器不知道number number的结果,所以这是它(解析器)的另一个选择

number [mul * number]

number mul

???

现在我们遇到了一点困难! 解析器不知道如何处理number mul。我们可以区分这种情况,但是如果我们继续探索下去,就会发现有很多不同的没有考虑到得可能,比如mul number, add number, add add, 等等。

那么我们应该怎么做呢?

幸运的是,我们可以做一点小“把戏”:我们可以认为一个number本身是一个乘积,并且一个乘积本身是一个和!

这种思路一开始看起来有点古怪,不过它的确是有意义的:

add:add' 'mul

|mul' 'mul

|mul

;

mul:mul'*'number

|number'*'number

|number

;

但是如果 mul能够变成 add, 且 number能够变成 mul , 有些行的内容就变得多余了。丢弃它们,我们就得到了:

add:add' 'mul

|mul

;

mul:mul'*'number

|number

;

让我们来使用这种新的语法来模拟运行一下1 2*3*4:

number number * number * number

现在没有一个规则是对应number*number的了,但是解析器可以“变得有创造性”

number [number] * number * number

number [mul * number] * number

number [mul * number]

[number] mul

[mul] mul

[add mul]

add

成功了!!!

如果你觉得这个很奇妙,那么尝试着去用另一种算数表达式来模拟运行一下,然后看看表达式是如何用正确的方式来一步步解决问题的。或者等着阅读下一节中的内容,看看计算机是如何一步步运行出来的!

运行解析器

现在我们对于如何让我们的语法运作起来已经有了非常不错的想法了,那就写一个实际的语法来应用一下吧:

start:add;// 这是最高层

add:add add_symbolmul|mul;

mul:mul mul_symbolnumber|number;

number:'[d.] ';// 十进制数的正则表达式

mul_symbol:'*'|'/';// Match * or /

add_symbol:' '|'-';// Match or -

你可能想要复习一下正则表达式,但不管怎样,这个语法都非常直截了当。让我们用一个表达式来测试一下吧:

Tag标签:

如何制作python代码_如何使用50行Python代码制作一个计算器相关推荐

  1. python9行代码_如何用9行Python代码编写一个简易神经网络

    原标题:如何用9行Python代码编写一个简易神经网络 Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发. 学习人工智能时,我给自己定了一个目标--用Pyth ...

  2. 50行python代码自动生成文章_如何通过50行Python代码获取公众号全部文章

    前言 我们平时阅读公众号的文章会遇到一个问题--阅读历史文章体验不好. 我们知道爬取公众号的方式常见的有两种:通过搜狗搜索去获取,缺点是只能获取最新的十条推送文章.通过微信公众号的素材管理,获取公众号 ...

  3. python30行代码_仅利用30行Python代码来展示X算法

    假如你对数独解法感兴趣,你可能听说过精确覆盖问题.给定全集 X 和 X 的子集的集合 Y ,存在一个 Y 的子集 Y*,使得 Y* 构成 X 的一种分割. 这儿有个Python写的例子. X = {1 ...

  4. python200行代码_如何用200行Python代码“换脸”

    本文将介绍如何编写一个只有200行的Python脚本,为两张肖像照上人物的"换脸". 这个过程可分为四步: 检测面部标记. 旋转.缩放和转换第二张图像,使之与第一张图像相适应. 调 ...

  5. html搜索框代码_解放双手 | 10行Python代码实现一款网页自动化工具

    各种各样的网站在我们日常工作和学习中占据着举足轻重的地位,学习.影音娱乐.查询资料.协同办公,越来越多的任务都被迁移到浏览器. 因此,网页也蕴含着很多有价值.我们能够用得到的资源.例如,数据.歌曲.影 ...

  6. python好看图案的编程代码_只需15行Python代码,实现图像定位功能

    引言本教程使用的环境:Windows 10 + Python 3.6 数据源:The Oxford-IIIT Pet Dataset 需要的第三方库:import tensorflow as tf i ...

  7. html小游戏代码_厉害!84 行 JavaScript 代码实现塔式堆叠游戏

    作者 | 李雪敬出品 | 程序人生(coder_life) 塔式堆叠小游戏游戏玩法想必大家对这个小游戏都挺熟悉的.这个游戏的重点是把尽可能多的盒子叠在一起.当你点击屏幕时,位于上方正在移动的盒子会往下 ...

  8. 50行python游戏代码_使用50行Python代码从零开始实现一个AI平衡小游戏

    使用50行Python代码从零开始实现一个AI平衡小游戏 发布时间:2020-10-23 09:26:14 来源:脚本之家 阅读:74 集智导读: 本文会为大家展示机器学习专家 Mike Shi 如何 ...

  9. 50行的python游戏代码_使用50行Python教AI玩运杆游戏

    编译:yxy 出品:ATYUN订阅号 嗨,大家好!今天我想展示如何使用50行Python代码教一台机器来平衡杆!我们将使用标准的OpenAI Gym作为我们的测试环境,并只使用numpy创建我们的智能 ...

最新文章

  1. linux学习-awk工具
  2. 调用实现天气预报功能android,Android编程实现获取新浪天气预报数据的方法
  3. Intel发布神经网络压缩库Distiller:快速利用前沿算法压缩PyTorch模型
  4. nat端口限制_Cisco ASA 防火墙 NAT - 基本概念
  5. 面试 -- 多线程( 一) -- 基础
  6. 爬虫篇——代理IP爬取备用及存储
  7. 暴力破解(初级)以及弱口令工具的使用
  8. html隐藏层点击显示不出来,[js+css]点击隐藏层,点击另外层不能隐藏原层
  9. NYOJ74 - 小学生算术
  10. CetnOS 6.7安装Hive 1.2.1
  11. JSP版LCX:端口转发神器 KPortTran
  12. java高级工程师个人简历模板
  13. 阿里矢量图iconfont的两种使用方法
  14. java中int与byte相互转换
  15. 产品经理如何写好一份简历
  16. 模拟电路 二极管伏安特性(一)
  17. 从二维码图片到镂空二维码stl模型(1)
  18. 超简单Python实现微信机器人自动回复
  19. 使用ffmpeg合并多个视频文件
  20. raspberry pi设置静态IP地址

热门文章

  1. IIS报错,App_global.asax.×××.dll拒绝访问
  2. pku 2195 Going Home 最小费最大流问题
  3. ExtAspNet v3.1.1
  4. 静静守候属于我们的幸福。坚守我们的约定
  5. C#实现的一个内存Ini类
  6. 正则表达式,VI,SED及shell编程2010-12-05
  7. 在linux下安装JDK
  8. java月份去0_java – 使用月份解析日期而不是前导0
  9. timestamp 转换 mysql_技术分享 | MySQL:timestamp 时区转换导致 CPU %sys 高的问题
  10. MRP Overview