惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

量子位
S
Securelist
MyScale Blog
MyScale Blog
Jina AI
Jina AI
罗磊的独立博客
The Cloudflare Blog
美团技术团队
博客园 - 叶小钗
阮一峰的网络日志
阮一峰的网络日志
博客园 - 三生石上(FineUI控件)
月光博客
月光博客
雷峰网
雷峰网
小众软件
小众软件
aimingoo的专栏
aimingoo的专栏
大猫的无限游戏
大猫的无限游戏
博客园 - Franky
博客园 - 聂微东
Y
Y Combinator Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
MongoDB | Blog
MongoDB | Blog
T
Tailwind CSS Blog
Attack and Defense Labs
Attack and Defense Labs
博客园_首页
Latest news
Latest news
Apple Machine Learning Research
Apple Machine Learning Research
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
The Hacker News
The Hacker News
G
GRAHAM CLULEY
Simon Willison's Weblog
Simon Willison's Weblog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
P
Proofpoint News Feed
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
U
Unit 42
D
Docker
Webroot Blog
Webroot Blog
N
Netflix TechBlog - Medium
T
Tor Project blog
C
Cyber Attacks, Cyber Crime and Cyber Security
L
LINUX DO - 最新话题
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
The Last Watchdog
The Last Watchdog
B
Blog
Recent Announcements
Recent Announcements
GbyAI
GbyAI
Microsoft Azure Blog
Microsoft Azure Blog
Security Latest
Security Latest
V2EX - 技术
V2EX - 技术
N
News | PayPal Newsroom
Microsoft Security Blog
Microsoft Security Blog

博客园 - yushih

初学Erlang,写两个程序玩玩 Django杂记: super与metaclass,locmem有害 一直没弄明白的事--为什么招聘要求会有“面向对象分析与设计”这一项 用Linux的iptables和Python模拟广域网 Python programming with goto 实用主义之过--Pragmatic Version Control using Subersion, 2nd Ed.的书评 信春哥!Python递归原地满状态变显式堆栈!入教即送尾递归优化! STL的binary search算法正确性的初步说明 关于文档标准之争的一点旁注 有关Ruby eval的一点编程风格 Design pattern一来,动态语言就笑了 - yushih - 博客园 完全没有领会“电子商务”的真谛 超级奇怪的F#格式错误 JAOO的魅力所在 一个Ruby idiom F#的一点糖 C++ hack:将C++编译器的类型检查转化为SLR(1)解析器 Quotes The Ruby Programming Language第一版非官方修正
Python把C语言打得满地找牙
yushih · 2010-01-23 · via 博客园 - yushih

好吧,我承认我又标题党了,不该把Python拉出来和C比,我绝无轻视C语言的意思。我想说的只是,在解决某些问题是,比起用C,用Python真是太舒服了。

《Beautiful Code》开篇第一章是由大神Brian Kernighan讲解另一位半神半程序员Rob Pike设计的C语言“正则”表达式引擎(“正则“二字打引号的原因下面会说明)。不能不说短短几十行代码是让人叹为观止的大师杰作。 具体好在哪里我也不重复了,如果我都能对这段代码头头是道,那也不劳Kernighan亲自出场赞不绝口了。总之,他说:I was amazed by how compact and elegant this code was... 我觉得,既然Kernighan都如此赞赏,论“compat and elegant”,这一定是C语言的最高境界了。估计这也是程序设计的最高境界了吧?一段时间里,我深信不疑,直到有一天,我看见了这个Regular expression engine in 14 lines of Python。再次惊叹。

首先,整理一下这段代码,实际上,原帖中的代码还不够精简,因为它用3行实现了一个Python标准库中已有的函数。取消这三行,改为用import引入库函数,得到的代码如下:

from itertools import chain as iconcatdef nil(s): yield sdef seq(l, r):
    
return lambda s: (sr for sl in l(s) for sr in r(sl))def alt(l, r):
    
return lambda s: iconcat(l(s), r(s))def star(e):
    
return lambda s: iconcat(nil(s), seq(e, star(e))(s))def plus(e): return seq(e, star(e))def char(c):
    
def match(s):
        
if s and s[0] == c: yield s[1:]
    
return match

就代码量来看,和《Beautiful Code》中的C“正则”引擎相差不大,但此Python实现的正则引擎在功能和简单性上都有无可比拟的优势,在分析这个正则引擎如何使用,顺带说明其工作原理后,再来看它的好处。

使用这个正则引擎时首先用char, nil, sq, alt, star, plus这几个函数构造出一个正则表达式,用BNF记号表示,格式是这样的:

exp -> char(c) |
       nil |
       seq(exp, exp) |             
       alt(exp, exp) |
       star(exp) |
       plus(exp)

其语义为:

exp->char(c) 表示匹配以字母c开头的字符串;

exp->nil 表示匹配一个空字符串;

exp->seq(exp1, exp2)表示如果exp1匹配s1,exp2匹配s2,则exp匹配由s1和s2连接成的字符串;

exp->alt(exp1, exp2)表示字符串s必须匹配exp1或exp2中的一个;

exp->star(exp1)匹配空字符串,或由一个或多个匹配exp1的子字符串连接成的字符串;

exp->plus(exp1)匹配由一个或多个匹配exp1的子字符串连接成的字符串。

总之不论格式还是语义它都和教科书上的正则表达式完全一致。例如,正则表达式e=c(a|d)*r用这个正则引擎表到出来就是:

e = seq(char('c'),

                seq(plus(alt(char('a'), char('d'))),

                        char('r'))

现在是使用的问题了。构造出来的正则表达式的Python类型是函数,此函数接受一个参数,即被匹配的字符串。调用此函数得到的结果是一个集合,其中每一个元素为一个字符串,对应一个正则表达式和目标字符串的匹配,而这个字符串的内容就是目标字符串中剩下的无法匹配的部分。也就是说,如果正则表达式可以和目标字符串匹配,则返回的集合不为空,反之则得到一个空集合,因此,我们可以用:

if e(str):

来判断正则表达式e是否和字符串str匹配。

要理解这个正则引擎的工作原理,只要抓住前述的一点“调用此函数得到的结果是一个集合,其中每一个元素为一个字符串,对应一个正则表达式和目标字符串的匹配,而这个字符串的内容就是目标字符串中剩下的无法匹配的部分“。实际上nil, char, seq, alt, plus中的每一个都符合此定义。这也是此Python正则引擎设计的好处之一:简单明了,易于理解,易于实现,易于确保正确。要写出《Beautiful Code》中的正则引擎是相当难的。我承认,以我的水平,即使Rob Pike把他的设计告诉我让我写代码,我也无法正确的写出。循环、指针还有边界条件,实在太容易出错了,这样的代码,非出自大师之手不可。而Python版的正则引擎就不一样,简单清晰的语义,很好的模块性,即使让我来写也可一次写对。

虽然代码量相当,但是此Python 在功能上大大强过C版。首先,C版并不是一个真正的正则引擎。它无法表达“匹配exp1或匹配exp2”,这大大的限制它的实用性,而Python版是教科书式的标准正则引擎。其次,C版不能表达(abc)*这样的表达式,即其Kleene closure中的内容只能是一个字母,而Python版Kleene closure中可以是任何表达式。这就是为什么开头“正则”二字要加引号。

此Python版代码的可组合性也比C版好。如果要在原有的基础上添加功能,Python版的用户只需要添加新的函数,而C版则要修改已有函数,难易程度不可同日耳语。比如注意到Python版没有“匹配任意字符”的功能,我们只需添加两行代码即可实现此功能:

def any(s):
    yield s[1:]

不仅实现简单,而且因为不改动已有代码,测试也容易。

为什么在实现一个简单的正则引擎这个任务上,Python比C版顺手许多呢。看看Python版中用到的C所不具备的语言特性:generator function, generator expression, 高阶函数,string slicing。string slicing不仅仅是一个简单的产生子字符串的表达式,支撑其易用性是Python的自动内存管理。