python写cdr插件_(如何(用Python)写一个(Lisp)解释器(下))

2号语言:完整的Lispy

现在我们来加上3个新的语法形式,构造一个更加完整的Scheme子集:

lambda特殊形式会创建一个过程(procedure)。(lambda这个名字来源于Alonzo Church的lambda calculus)

lis.py> (define circle-area (lambda (r) (* pi (* r r)))

lis.py> (circle-area 10)

314.159265359

过程调用(circle-area 10)使我们对过程的主体部分(* pi (* r r))进行求值。求值所在的环境中pi与*的值同全局环境相同,而r的值为10。事实上,解释器并不会简单地在全局环境之中将r的值设为10。如果我们将r用于其他用途会怎么样?我们不希望对circle-area的调用改变r的值,因此我们希望将一个局部变量r设为10,这样就不会影响到其他同名的变量。因此,我们需要构建一种新的环境,允许同时创建局部和全局变量。

想法如下:在我们对(circle-area 10)求值时,首先提取过程主体部分(* pi (* r r)),随后在仅有一个本地变量r的环境中求值,但该环境同时也能访问全局环境。下图演示了这种环境模型,局部环境(蓝色)嵌套在全局环境(红色)之中:

当我们在一个被嵌套的环境中查找变量时,首先在本层查找,如果没有找到对应值的话就到外一层查找。

显然,过程和环境相关,所以我们把他们放到一起:

class Procedure(object):

"用户定义的Scheme过程。"

def __init__(self, parms, body, env):

self.parms, self.body, self.env = parms, body, env

def __call__(self, *args):

return eval(self.body, Env(self.parms, args, self.env))

class Env(dict):

"环境是以{'var':val}为键对的字典,它还带着一个指向外层环境的引用。"

def __init__(self, parms=(), args=(), outer=None):

self.update(zip(parms, args))

self.outer = outer

def find(self, var):

"寻找变量出现的最内层环境。"

return self if (var in self) else self.outer.find(var)

global_env = standard_env()

我们看到每个过程有3个组成部分:一个包含变量名的列表,一个主体表达式,以及一个外层环境。外层环境使得我们在局部环境中无法找到变量时有下一个地方可以寻找。

环境是dict的子类,因此它含有dict拥有的所有方法。除此之外还有两个额外的方法:构造器__init__接受一个变量名列表及对应的变量值列表,构造创造一个新环境,内部形式为{variable: value}键对,并拥有一个指向外层环境的引用。

find函数用于找到某个变量所在的正确环境,可能是内层环境也可能是更外层的环境。

要想知道这部分的工作原理,我们首先来看看eval的定义。注意,现在我们需要调用env.find(x)来寻找变量处于哪一层环境之中;随后我们才能从那一层环境之中提取x。(define分支的定义没有改变,因为define总是向最内一层的环境添加变量。)同时我们还增加了两个判定分支:set!分支中,我们寻找变量所处的环境并将其设为新的值。通过lambda,我们可以传入参数列表、主体以及环境以创建一个新的过程。

def eval(x, env=global_env):

"在某环境中对一个表达式进行求值。"

if isinstance(x, Symbol): # 变量引用

return env.find(x)[x]

elif not isinstance(x, List): # 直面产量

return x

elif x[0] == 'quote': # 引用

(_, exp) = x

return exp

elif x[0] == 'if': # 条件判断

(_, test, conseq, alt) = x

exp = (conseq if eval(test, env) else alt)

return eval(exp, env)

elif x[0] == 'define': # 定义

(_, var, exp) = x

env[var] = eval(exp, env)

elif x[0] == 'set!': # 赋值

(_, var, exp) = x

env.find(var)[var] = eval(exp, env)

elif x[0] == 'lambda': # 过程

(_, parms, body) = x

return Procedure(parms, body, env)

else: # 过程调用

proc = eval(x[0], env)

args = [eval(arg, env) for arg in x[1:]]

return proc(*args)

为了更好地理解过程和环境是怎样协同运作的,我们来看看下面这段程序。思考一下,在我们对(account1 -20.00)求值的时候,程序会生成一个怎样的环境呢?

每个矩形框代表一个环境,环境的颜色和程序中新定义变量的颜色相对应。在程序的最后两行中,我们定义了account1并调用了(account1 -20.00),这表示我们创建了一个拥有100美金余额的账户,并从中取出20美金。在对(account1 -20.00)进行求值的过程中,我们会对黄色高亮部分进行求值。该表达式中有三个变量:amt可以直接在最内层环境(绿色)中找到。但balance不在那一层环境之中,我们需要查找绿色的外一层环境(蓝色)。然而变量‘+’依然不能在这两个环境之中找到,所以我们需要在更外一层环境中寻找(红色的全局环境)。这一先在内层环境查找,再在外层环境中查找的方式被称为“词法作用域”(Lexical Scoping)。Env.find(var)依照词法作用域规则查找变量所处的正确环境。

现在我们能做的事又多了不少:

>>> repl()

lis.py> (define circle-area (lambda (r) (* pi (* r r))))

lis.py> (circle-area 3)

28.274333877

lis.py> (define fact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1))))))

lis.py> (fact 10)

3628800

lis.py> (fact 100)

9332621544394415268169923885626670049071596826438162146859296389521759999322991

5608941463976156518286253697920827223758251185210916864000000000000000000000000

lis.py> (circle-area (fact 10))

4.1369087198e+13

lis.py> (define first car)

lis.py> (define rest cdr)

lis.py> (define count (lambda (item L) (if L (+ (equal? item (first L)) (count item (rest L))) 0)))

lis.py> (count 0 (list 0 1 2 3 0 0))

3

lis.py> (count (quote the) (quote (the more the merrier the bigger the better)))

4

lis.py> (define twice (lambda (x) (* 2 x)))

lis.py> (twice 5)

10

lis.py> (define repeat (lambda (f) (lambda (x) (f (f x)))))

lis.py> ((repeat twice) 10)

40

lis.py> ((repeat (repeat twice)) 10)

160

lis.py> ((repeat (repeat (repeat twice))) 10)

2560

lis.py> ((repeat (repeat (repeat (repeat twice)))) 10)

655360

lis.py> (pow 2 16)

65536.0

lis.py> (define fib (lambda (n) (if (< n 2) 1 (+ (fib (- n 1)) (fib (- n 2))))))

lis.py> (define range (lambda (a b) (if (= a b) (quote ()) (cons a (range (+ a 1) b)))))

lis.py> (range 0 10)

(0 1 2 3 4 5 6 7 8 9)

lis.py> (map fib (range 0 10))

(1 1 2 3 5 8 13 21 34 55)

lis.py> (map fib (range 0 20))

(1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765)

如此一来,我们的语言中就有了过程、变量、条件判断(if)和顺序执行(begin)。如果你熟悉其他语言的话,你可能会觉得我们还需要while或者for循环,但Scheme认为自己不需要这两种循环结构。Scheme标准中说:“Scheme展示了只需要极少量构造表达式的规则,无需规定表达式的组成方式,就足以构建出一个实用而高效的语言。”在Scheme中,我们通过构建递归函数的方式来实现迭代。

Lispy有多小/快/完整/好?

我们以以下的标准来评判Lispy:小:Lispy十分简短:不算空行和注释的话一共只有117行,源码只有4K大小。(更早的一个版本只有90行,但包含的标准过程更少,也显得过于简陋了。)我用Java实现的最小Scheme(Jscheme)包含1664行代码,源码有57K大。Jscheme之前叫SILK(Scheme in Fifty Kilobytes,缩写对不上...anyway),不过实际上只有在编译成bytecode的情况下才小于50k。在“小”这一方面,Lispy做得要好很多,我想它符合Alan Kay在1972年所说的:“你可以用‘一页代码’定义出‘世界上最强大的语言’”。(其实我觉得Alan Kay本人不会同意,因为Python解释器的代码量远高于一页。)

快:Lispy可以在0.003秒内计算出(fact 100)的值。对我来说够快了(尽管比大部分语言慢很多)。

完整:Lispy和Scheme标准比起来算不上完整。一下是主要的一些缺少处:语法:缺少注释,quote符和quasiquote符,#常量,延伸的表达式(从if中延伸出的cond,从lambda中延伸出的let),和dotted list标记。

语义:缺少call/cc和尾递归。

数据类型:缺少String, character, boolean, ports, vectors, exact/inexact numbers。Python的列表相比于Scheme里的列表实际上更接近于Scheme里的vector。

过程:少了100多种原始过程:包含与所有缺少的数据类型有关的过程,以及set-car!和set-cdr!之类的过程,因为我们没法用Python列表直接实现这一功能。

错误修复:Lispy不会尝试去侦测,报告以及修复错误。想用Lispy编程的话你需要一个从不犯错的程序员。

好:这项的评判就交由读者去定了。对我来说,它不错地完成了预定目标——解释Lisp解释器的工作原理。

附录:完整代码

################ Lispy: Scheme Interpreter in Python

## (c) Peter Norvig, 2010-16; See http://norvig.com/lispy.html

from __future__ import division

import math

import operator as op

################ Types

Symbol = str # A Lisp Symbol is implemented as a Python str

List = list # A Lisp List is implemented as a Python list

Number = (int, float) # A Lisp Number is implemented as a Python int or float

################ Parsing: parse, tokenize, and read_from_tokens

def parse(program):

"Read a Scheme expression from a string."

return read_from_tokens(tokenize(program))

def tokenize(s):

"Convert a string into a list of tokens."

return s.replace('(',' ( ').replace(')',' ) ').split()

def read_from_tokens(tokens):

"Read an expression from a sequence of tokens."

if len(tokens) == 0:

raise SyntaxError('unexpected EOF while reading')

token = tokens.pop(0)

if '(' == token:

L = []

while tokens[0] != ')':

L.append(read_from_tokens(tokens))

tokens.pop(0) # pop off ')'

return L

elif ')' == token:

raise SyntaxError('unexpected )')

else:

return atom(token)

def atom(token):

"Numbers become numbers; every other token is a symbol."

try: return int(token)

except ValueError:

try: return float(token)

except ValueError:

return Symbol(token)

################ Environments

def standard_env():

"An environment with some Scheme standard procedures."

env = Env()

env.update(vars(math)) # sin, cos, sqrt, pi, ...

env.update({

'+':op.add, '-':op.sub, '*':op.mul, '/':op.truediv,

'>':op.gt, '=':op.ge, '<=':op.le, '=':op.eq,

'abs': abs,

'append': op.add,

'apply': apply,

'begin': lambda *x: x[-1],

'car': lambda x: x[0],

'cdr': lambda x: x[1:],

'cons': lambda x,y: [x] + y,

'eq?': op.is_,

'equal?': op.eq,

'length': len,

'list': lambda *x: list(x),

'list?': lambda x: isinstance(x,list),

'map': map,

'max': max,

'min': min,

'not': op.not_,

'null?': lambda x: x == [],

'number?': lambda x: isinstance(x, Number),

'procedure?': callable,

'round': round,

'symbol?': lambda x: isinstance(x, Symbol),

})

return env

class Env(dict):

"An environment: a dict of {'var':val} pairs, with an outer Env."

def __init__(self, parms=(), args=(), outer=None):

self.update(zip(parms, args))

self.outer = outer

def find(self, var):

"Find the innermost Env where var appears."

return self if (var in self) else self.outer.find(var)

global_env = standard_env()

################ Interaction: A REPL

def repl(prompt='lis.py> '):

"A prompt-read-eval-print loop."

while True:

val = eval(parse(raw_input(prompt)))

if val is not None:

print(lispstr(val))

def lispstr(exp):

"Convert a Python object back into a Lisp-readable string."

if isinstance(exp, List):

return '(' + ' '.join(map(lispstr, exp)) + ')'

else:

return str(exp)

################ Procedures

class Procedure(object):

"A user-defined Scheme procedure."

def __init__(self, parms, body, env):

self.parms, self.body, self.env = parms, body, env

def __call__(self, *args):

return eval(self.body, Env(self.parms, args, self.env))

################ eval

def eval(x, env=global_env):

"Evaluate an expression in an environment."

if isinstance(x, Symbol): # variable reference

return env.find(x)[x]

elif not isinstance(x, List): # constant literal

return x

elif x[0] == 'quote': # (quote exp)

(_, exp) = x

return exp

elif x[0] == 'if': # (if test conseq alt)

(_, test, conseq, alt) = x

exp = (conseq if eval(test, env) else alt)

return eval(exp, env)

elif x[0] == 'define': # (define var exp)

(_, var, exp) = x

env[var] = eval(exp, env)

elif x[0] == 'set!': # (set! var exp)

(_, var, exp) = x

env.find(var)[var] = eval(exp, env)

elif x[0] == 'lambda': # (lambda (var...) body)

(_, parms, body) = x

return Procedure(parms, body, env)

else: # (proc arg...)

proc = eval(x[0], env)

args = [eval(exp, env) for exp in x[1:]]

return proc(*args)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
主要功能:理论上,该程序可以解码任何基于BER编码的CDR文件。不过,由于我在工作中只接触到Ericsson的某些版本的CDR,而且,用于解析CDR文件的描述文件来源于供应商,我的程序也只能识别来自于这个供应商的描述文件,所以,移植到其它厂商的环境下可能会产生一些问题。希望大家试用过后能够把各种情况反馈给我,最好能够把BER编码的CDR样本及其描述文件发给我,这样我就能够作为依据,完善这个程序了。 主要特点: 1. 高速!该程序比目前我在网上见到过的所有能够使用的同类型程序都要快,甚至在同等条件下,比我所在公司的供应商提供的程序还要快15%到20% 2. 支持关键字搜索。不需要把整个CDR文件的记录全部翻译成文本记录再逐个查找,而是在解码的过程中顺便完成搜索,节省时间和空间 3. 直接支持压缩格式。通常CDR文件都是经过压缩后保存在磁盘的,该程序可以直接读取经过压缩后的CDR文件,不需要把它首先解压缩,然后再解码,进一步提供效率。同时,这个程序也支持压缩输出文件,为用户提供方便。目前支持的压缩格式有gzip和bzip2 最新版本: 1. destroy 1.0.0 for Linux 2. destroy 1.0.0 for Solaris 3. destroy 1.0.0 for Tru64 依赖关系:由于该程序支持压缩格式,因此它依赖于libz和libbz2 使用方法:键入./destroy –h可以获得所有帮助信息 $./destroy -h ./destroy 1.0.0 (2010-06-28) This is a program decoding a BER encoded CDR file Author: Roy Liang Blog: http://blog.csdn.net/yui/ Usage: ./destroy -f <file_name> -o <output_name> [-c <config_name>] [-k <keyword>] [-z] [-j] -f --file the CDR file to be decoded -o --output the output file in plain text format -c --config the description file of the CDR file, if not given, use default configuration -k --keyword the keyword to search, if not given, all records will be written into output file -z --gzip gzip the result -j --bzip2 bzip2 the result -h --help print this page
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值