编译原理 2:为什么编程语言都没有阶乘运算符?

问题

昨天发布了一篇文章《编译原理极简入门:表达式求值》,我们用编译原理的方式实现了表达式求值。有一位朋友说,如何实现阶乘运算呢?我当时在地铁上,只是简单考虑阶乘这个优先级来说,应该是高于乘除法的,那么他应该被定义在 factor 里面。但是深入再想,发现问题并不简单。

首先尝试实现阶乘

在数学公式里面 n 的阶乘被表示为 n!,那么我们先考虑一下如何进化我们的 BNF 让他支持阶乘,之前我们的 BNF 是这样的:

BNF 复制代码
factor => NUM | ( expression ) | -factor 

首先负数是没有阶乘的,直接排除 -factor 这个分支,那我们的阶乘 factor 可以这样进化:

BNF 复制代码
factor => NUM | NUM! | ( expression ) | (expression)! | -factor 

为了简化问题,我们先实现 NUM! 感受一下会发生什么。

代码实现

Python 复制代码
def factor(i):
    if tokens[i].isdigit():
        i += 1
        if tokens[i] == '!':
            n = int(tokens[i])
            i += 1
            return i, factorial(n)
        return i+1, int(tokens[i])
    elif tokens[i] == "-":
        i += 1
        i, a = factor(i)
        return i, -1*a
    elif tokens[i] == "(":
        i += 1
        i, a = expression(i)
        if tokens[i] == ")":
            i += 1
            return i, a
        else:
            raise Exception("SyntaxError: near the '%s'"%(tokens[i]))
    else:
        raise Exception("SyntaxError: near the '%s'"%(tokens[i]))
# 计算阶乘
def factorial(n): 
    if n == 0: 
        return 1 
    else: 
        return n * factorial(n-1)

我们发现之前的函数里面,拿到 NUM 就可以直接返回了,现在要多一次判断。而这个 if 看似不起眼,实则影响巨大,他的出现使得 factor 这个函数的过程产生了回溯 。说人话就是,正常情况拿完 NUM 就返回了,但因为阶乘这个后置的一元运算符,使得我们每次处理 + - * / 的时候都要拿出来看一看,如果是阶乘则由 factor 处理,如果不是,则会把这个符号放回待处理交给其他语法去处理。如果括号后面也允许阶乘的话,也要做同样的事情。阶乘作为一个冷门算符,如果为了支持这样一个算符而增加大量的回溯,就非常影响编译的效率了。

因此,所有主流的语言都不会实现后置的一元运算符,像阶乘这样的计算,应该用函数实现。

相关推荐
LYFlied8 天前
Webpack 深度解析:从原理到工程实践
前端·面试·webpack·vite·编译原理·打包·工程化
LYFlied8 天前
从循环依赖检查插件Circular Dependency Plugin源码详解Webpack生命周期以及插件开发
前端·webpack·node.js·编译原理·plugin插件开发
LYFlied9 天前
有限状态机FSM工作原理详解及Babel中的有限状态机
前端·面试·编译原理·状态机·babel·打包原理
中杯可乐多加冰11 天前
openEuler开发环境:GCC编译构建效率基准测试
编译原理
LYFlied11 天前
前端开发者需要掌握的编译原理相关知识及优化点
前端·javascript·webpack·性能优化·编译原理·babel·打包编译
colus_SEU12 天前
【编译原理笔记】5.3 Intermediate Code Generation
笔记·编译原理
colus_SEU14 天前
【编译原理笔记】5.2 SDT Schemes
笔记·编译原理
colus_SEU2 个月前
【编译原理笔记】3.4 Tokens Recognization
笔记·编译原理
colus_SEU2 个月前
【编译原理笔记】3.3 Specification of Tokens
笔记·编译原理
朝与暮2 个月前
《深入浅出编译原理 -- 编译原理总述(一)》
前端·编译原理·编译器