首页 > 编程语言 >华为机试:仿 LISP 运算 - Python实现之篇3

华为机试:仿 LISP 运算 - Python实现之篇3

时间:2024-12-26 09:27:00浏览次数:5  
标签:elif 之篇 LISP expr args expt Python env return

篇1中可以将字符串解析成Python的list的形式, 用编程术语叫做: 解析出语法树.
篇2中可以实现表达式的求值. 根据操作符, 跳转到相应的求值分支.
以上功能, 仅仅实现了一个计算器的功能. 离变成编程语言还差了: 函数定义和调用. 那么,
篇3来实现函数定义, 即lambda的定义与解释.

import re

# 解析字符串(源代码), 生成树法树(AST)
def parse(expt, exception=True):
    expt = re.sub("\(", " ( ", expt)
    expt = re.sub("\)", " ) ", expt)
    res = []
    for i in expt.strip().split():
        if i != ')':
            if re.match("^[0-9]+$", i):
                i = int(i)
            elif re.match("^[0-9][0-9.]*$", i):
                i = float(i)
            res.append(i)
        if i == ')':
            tmp = []
            while res and res[-1] != '(':
                tmp.insert(0, res.pop())
            if res: res[-1] = tmp
    if '(' in res:
        if exception:
            print('parse error: 括号不匹配')
            raise ValueError()
    return res

# 求值函数eval(): 解析每个语句中的基本元素, 函数名,变量名,立即数,运算符号等.
#   然后调用apply()执行
global_env={}
def eval(expt, env):
    if type(expt) is list:
        if expt[0] == 'define':         # 添加一个全局条目
            env[expt[1]] = eval(expt[2], env)
            print('new symbol defined: %s'% expt[1])
        elif expt[0] == 'quote':
            return expt[1]
        elif expt[0] == 'lambda':       # lambda和当时的环境形成一个闭包!
            saved_env = env.copy()      #   (变量的捕获)
            return [expt[0], expt[1], expt[2:], saved_env]
        elif expt[0] == 'cond':         # 分支结构,相当于switch...case
            for p,t in expt[1:]:        #   遍历所有条件,找到第一个True,或else
                if p=='else' or eval(p, env)==True:
                    return eval(t, env)
        else:                           # # 递归解析所有符号, 然后apply执行
            expt = [eval(i, env) for i in expt]
            return apply(expt[0], expt[1:])
    elif type(expt) is int or type(expt) is float:
        return expt                     # int/float条目返回自身即可
    elif expt in ['+','-','*','/','=','<','>', 'zero?', 'null?']:
        return [expt]                   # 基本运算符返回自身
    elif expt in env:
        return env[expt]                # 先搜索: 局部变量/符号
    elif expt in global_env:
        return global_env[expt]         # 后搜索: 全局变量/符号, 所以局部变量覆盖全局变量
    else:
        print('symbol not defined: %s'%expt)
        raise NameError()

def apply(expr, args):
    if   expr[0] == '+': return args[0] + args[1]
    elif expr[0] == '-': return args[0] - args[1]
    elif expr[0] == '*': return args[0] * args[1]
    elif expr[0] == '/': return args[0] / args[1]
    elif expr[0] == '=': return args[0] == args[1]
    elif expr[0] == '<': return args[0] < args[1]
    elif expr[0] == '>': return args[0] > args[1]
    elif expr[0] == 'zero?': return 0 == args[0]
    elif expr[0] == 'null?': return 0 == len(args[0])
    elif expr[0] == 'lambda':
        pars, body, env = expr[1:]
        for k,v in zip(pars, args): env[k] = v  # 形参/实参->env表
        ##print ('lambda', pars, body, '----', env)	# debug
        for i in body: ret = eval(i, env)       # 求值
        return ret                              # 返回

def run(code):
    try:
        ast = parse(code)                       # 解析语法树
        for i in ast:                           # 爬树求值
            v = eval(i, global_env)
            if v is not None: print(v)
    except:
            pass

if __name__ == '__main__':
    print ('-'*60)
    print ('welcome to yet another lisp interpreter by jia.')
    print ('version 0.01')
    print ('have fun!')
    print ('-' * 60)
    while True:
        code = input("jia-λ > ")            #提示符:输入代码
        for i in range(99):
            ast = parse(code, exception=False)  #预解析
            unfold = (ast.count('('))       #未配对的'('表示未完待续
            indent = (9+3*unfold)*' '       #按未配对的'('的个数缩进
            if unfold == 0 or ';' in code:
                break
            code += ' ' + input(indent)     #下一行继续输入,自动缩进
        run(code)

以上代码和一上篇的代码, 主要增加了lambda的处理, 新增加代码如下:

def eval(expt, env):
    if type(expt) is list:
    ... ...
        elif expt[0] == 'lambda':       # lambda和当时的环境形成一个闭包!
            saved_env = env.copy()      #   (变量的捕获)
            return [expt[0], expt[1], expt[2:], saved_env]

处理lambda段, 是在当下环境构造一个闭包. 并返回这个闭包. 这里不求值.
碰到lambda关键字, 说明这一个段落为一个closure, 一些编程语言叫它闭包. 则在定义lambda的当下, 将当前的现场环境保存起来, 这个在一些编程语言中叫做变量捕获.
这里仅仅是把环境存起来, 并不着急着求出值. 这在一些编程语言中叫: 惰性求值.

那么在那里求值呢, 可以看到Apply一节有增加几行代码, 如下:

def apply(expr, args):
... ...
    elif expr[0] == 'lambda':
        pars, body, env = expr[1:]
        for k,v in zip(pars, args): env[k] = v  # 形参/实参->env表
        for i in body: ret = eval(i, env)       # 求值
        return ret                              # 返回

处理闭包, 则是解开闭包, 取出保存的环境, 完成求值并返回值.
lambda关键字后面是pars参数表, body函数体, env刚才打包的变量捕获. 这里pars/body/env都不是一个简单的值, 可能是一组值.

完成这些工作, 先code=‘(+ 2 3)’, 再run(code). 便 可以执行代码, 并输出结果了.
但感觉不优雅啊.

最后面的__main__函数, 则是写了一个主循环, 不停在从命令行接收用户输入的lisp语句, 并执行.
有了命令行输入的处理, 这便更像一个解释器了.

舒服了.

以上代码保存为xxx.py, 直接运行即可. 效果如下:
lisp基本用户测试

标签:elif,之篇,LISP,expr,args,expt,Python,env,return
From: https://blog.csdn.net/weixin_46766770/article/details/144729635

相关文章

  • 将Python模块打包为可直接运行的ZIP文件
    要使用zipapp将Python模块(例如位于E:\py\abc.py)打包为可直接运行的ZIP文件,你需要按照以下步骤进行操作:一、准备环境确保Python安装:你需要有Python解释器安装在你的系统上,因为zipapp是Python的一个标准库模块。准备项目文件:确保你的Python模块(如abc.py)以及任何依赖项都位于同一......
  • python多进程通过socket通讯
    服务进程和客户端同体,代码:importsocketimportmultiprocessingdefhandle_server(connection):data=connection.recv(1024)print("接收到客户端请求:",data.decode(),"\n")#发送数据connection.sendall('我是服务器进程,哈哈'.encode('u......
  • Python 有哪些常用的库
    Python拥有一个庞大的生态系统,其中包含了许多用于不同领域的库。以下是一些常用的Python库:1.标准库Python的标准库非常强大,包括了用于文件操作、系统调用、网络通信等的模块。2.Web开发Flask:一个轻量级的Web应用框架。Django:一个高级的Web框架,内置了用户认证、内......
  • python图片脚本4-批量图片加水印(详细注释+GUI界面+exe可执行文件)
    目录前言导航pillow库的使用篇tkiner库的使用篇图片脚本篇源码批量处理图片尺寸脚本源码效果GUI界面源码效果打包成.exe可执行文件共勉博客前言本文介绍一个用python第三方库pillow写的批量处理图片加水印的脚本,以及脚本对应的使用tkinter库写的GUI界面并把它打......
  • Python 抽象基类 ABC :从实践到优雅
    今天我们来聊聊Python中的抽象基类(AbstractBaseClass,简称ABC)。虽然这个概念在Python中已经存在很久了,但在日常开发中,很多人可能用得并不多,或者用得不够优雅。让我们从一个实际场景开始:假设你正在开发一个文件处理系统,需要支持不同格式的文件读写,比如JSON、CSV、XML等。......
  • Python数据分析_Pandas_数据分析入门_3
    文章目录今日内容大纲介绍1.DataFrame-保存数据到文件2.DataFrame-读取文件数据3.DataFrame-数据分析入门4.DataFrame-分组聚合计算5.Pandas-基本绘图6.Pandas-常用排序方法7.Pandas案例-链家数据分析7.Pandas案例-链家数据分析_GIF_demo了解数据df1.info()df1.describ......
  • python爬虫实验:用Python爬取链家指定数据--附完整代码(基于requests和BeautifulSoup实
    1、前言 本实验实现了对链家房屋名字,所在小区,装饰,是否核验,楼层,总楼层以及租金进行爬取,仅供学习使用。2、url分析第二页:https://cd.lianjia.com/ershoufang/pg2/第三页:https://cd.lianjia.com/ershoufang/pg3/故第i页的url为:https://cd.lianjia.com/ershoufang/pg{i}/......
  • Python的安装以及环境变量的配置
    一、python是什么及如何安装?Python是一种面向对象、解释型、动态类型计算机程序设计语言,是一种面向对象的动态类型语言;其能够把用其他语言制作的各种模块(尤其是C/C++)很轻松地联结在一起。二、安装步骤下载地址:https://www.python.org/downloads/windows/下载完成后......
  • Python变量与数据类型
    一、什么是Python变量1,变量就是数据的别名2,使用变量等同于使用数据3,随着程序的进行,变量发生变化#将数据10赋予变量(别名)a,可以使用a代替10a=10b=20c=30#通过print函数可以将多个内容输出到控制台print(a)print(b)print(c)print(a+b)print(a+c)print(b......
  • python多进程,通过内存共享来通信,使用进程锁来防止数据问题
    代码:importmultiprocessingimporttime'''使用锁和multiprocessing.Value,multiprocessing.Array,multiprocessing.Manager().list'''defworker1(shared_number1,lock):for_inrange(10):withlock:......