函数式编程思想及其解释器的实现
来源:互联网 发布:linux配置网卡 编辑:程序博客网 时间:2024/06/05 06:47
前言
函数表达式在Excel中有着非常重要的作用,excel的公式就是一种基于函数的表达式,但公式中支持诸如“+”、“-”等运算符。纯函数表达式应该只包含函数和参数。遵循这种思想我们可以将数学上的中缀表达式”(56-9)*3+1/6”使用函数的方式来表示:
ADD(MULTIPLY(MINUS(56,9),3),DIVIDE(1,6))
即每一种运算符都可以抽象为一个函数(比较类似于前缀表达式),这就是函数式编程思想。在函数式编程中,函数起到了决定性的作用,目前有很多著名的函数式编程语言(如LISP、F#),这里我要使用C#实现一个自己的函数式编程语言,在本文中,我将只实现“+”、“-”、“*”、“/”四个操作符函数。
基本思想
我们知道,每种语言都具有它自身的语法,我在这里实现的语言(我这里将其命名为PureF)的语法非常简单,它由函数名、数值、“(”、“)”、“,”这几种元素构成,于是我可以用Token来表示每种元素,后面我还将实现其Tokenizer(分词器)。下图为Token结构体:
对于上面例子中的函数表达式,我们可以构建它的AST(抽象语法树):
从图中可以看到,一个函数的参数也可以是一个函数,通过这种嵌套性,我们可以实现一些复杂的运算。
通过对语言元素进行文法分析,我们需要建立语言中存在的两种表达式:函数表达式、值表达式,函数表达式表示了一个函数的函数名及其参数列表,而值表达式则表示一个数值(我这里只先实现整数类型)。
使用这两种表达式,我们就可以建立语法树了。
在得到语法树后,触发根元素的计算方法,通过递归可以将所有参数计算为数值,最终求解。
解释器实现
首先解释几个概念:
1. Context(上下文)
顾名思义,它管理了整个翻译过程中所涉及的变量(包括全局和局部)、函数、堆栈、状态等,相当于一个容器。我们可以将一些自定义函数添加到上下文中,上下文是我们对语言进行扩展的入口。
2. Tokenization(分词操作)
一个表达式将许多语言元素混在一起,对人来说可以非常容易地理解,而对于计算机,我们需要通过程序来对一个表达式进行分割,以便于后续的工作
3. AST(语法树)
语法树可以表示一个具体的解释过程,计算将从树根开始,遇到函数节点将继续调用它的计算方法,这就相当于从树的最高级开始计算直到树根,最后将整个表达式计算完毕。
然后我们建立表示表达式的抽象类AbstractExpression及实现的整数表达式和函数表达式:
这里着重看一下函数表达式的实现:
从上面的图中我们可以看出Context的作用,表达式节点从Context中获取相应函数,并将参数代入执行。
接下来实现分词器,这里的分词器比较简单,我写的相对弱一些,至少可以看懂,思路就是建立一个临时可拼接字符串,普通字符直接压入字符串中,遇到左括号就将上一个字符串压入Tokens列表中,然后压入一个左括号标记,再清除上面的临时字符串。遇到右括号和逗号也是同理,这里要裁剪掉空白符,贴出代码:
最后如果临时字符串中还有非空白字符,也将其压入Tokens中
开始的例子中的那个表达式经过分词后将得到:
ADD ( MULTIPLY ( MINUS ( 56 , 9 ) , 3 ) , DIVIDE ( 1 , 6 ) )
然后用这些tokens,我们就可以开始分法分析了,也就是构建语法树。
简述一下思路:建立两个栈(分别是函数栈和参数栈),遇到数值压入参数栈,遇到左括号将上一token压入函数栈,遇到右括号先将上一个token压入函数栈顶(Peek操作)的函数,再将函数压入参数栈,遇到逗号则是将参数栈顶的参数压入函数栈顶的函数。最后将从参数栈中Pop得到根函数。
代码:
这里演示一下表达式”ADD(MULTIPLY(5,7),6)”的构建过程(F表示函数栈,A表示参数栈):
1. ADD
F: (Empty)
A: (Empty)
2. (
F: ADD
A: (Empty)
3. MULTIPLY
F: ADD
A: (Empty)
4. (
F: ADD MULTIPLY
A: (Empty)
5. 5
F: ADD MULTIPLY
A: 5
6. ,
F: ADD MULTIPLY(5)
A: (Empty)
7. 7
F: ADD MULTIPLY(5)
A: 7
8. )
F: ADD
A: MULTIPLY(5,7)
9. ,
F: ADD(MULTIPLY(5,7))
A: (Empty)
10. 6
F: ADD(MULTIPLY(5,7))
A: 6
11. )
F: (Empty)
A: ADD(MULTIPLY(5,7),6)
A栈中就有已经构建好的语法树。
触发跟函数的计算方法即可,至此,整个解释过程结束。
扩展解释器
现在实现的解释器所说可以构建出语法树,但是还不能计算出数值,因为我们还没有每种运算符的函数实现,所以我在这里实现了四则运算的函数:
然后使用委托,将函数进行封装,保存在Context的一个字典中,以供使用。
执行测试
将上面实现的方法封装成一个类,然后在主函数中构建测试:
测试几个表达式:
由于没有实现表示浮点的表达式,因此所有无法整除的数都会截掉小数部分。
完整的源码Available now:Github
- 函数式编程思想及其解释器的实现
- 函数式编程的思想
- 数据结构KMP算法中next函数的求解思想及其解释
- 冒泡排序函数及其编程思想
- 函数式编程思想
- 函数式编程思想
- 面向对象编程思想-解释器模式
- 面向对象编程思想-解释器模式
- 选择排序的思想及其实现
- 在java编程思想中对synchronized的一点解释:
- 在java编程思想中对synchronized的一点解释:
- python解释器实现及其嵌入式应用:解释器移植
- 解释器模式--注重的解释的思想
- 一个Excel技巧,及其蕴含的编程规范思想
- 一个Excel技巧,及其蕴含的编程规范思想
- 一个Excel技巧,及其蕴含的编程规范思想
- 什么是泛型编程思想?及其简单的应…
- Java编程思想--构造函数的重载
- SQL - waitfor delay/time(SQL中延迟时间的方法)
- Makefile资料
- Hive语言手册之一:CLI
- Android笔记--------SharedPreferences的Editor
- Java设计模式学习之工厂模式
- 函数式编程思想及其解释器的实现
- OpenCV学习笔记一
- UBUNTU12.04下安装配置体验gnome3
- 把鼠标移动到指定位置
- 第三周作业
- C++中的关联容器(一)
- 我现在很菜,但是我在努力让自己变得不那么菜
- Java RandomAccessFile使用
- 下拉框“数据字典”设计