肥宅钓鱼网
当前位置: 首页 钓鱼百科

dct算法的基本原理(编程界的二向箔)

时间:2023-07-10 作者: 小编 阅读量: 3 栏目名: 钓鱼百科

本篇文章主要讨论如何基于其中间语言,通过AST树的操作来进行元编程,并实现一些现有dart语法本身实现不了的能力。AST描述了每个表达式/语句中的子语句的执行顺序和执行逻辑,因而它可以被很方便地翻译成目标代码。这种方式其实就是基于AST库对Dill进行manipulation。这个库内的组件包含了所有AST树涉及到的节点的定义和访问,将类型,函数,语句,声明,表达式等编程基本概念抽象成了对象。更多的技巧可参考AST的定义。

阅读过《三体》的同学肯定知道“降维打击”,从更高维度看问题,直接将对手KO。今天我们闲聊一下编程界的“二向箔”——元编程。

1. 什么是元编程

我们听过了太多太多的名词,耳朵似乎都有点名词麻痹症了。比如,有些名词为了装x(比如笔者的文章标题...)或者其本身的意义难以定义,就会加上一些似乎闪烁着光芒的前缀——如meta。计算机软件这行业就有meta data, meta model, meta programming。

今天我们装x的主角就是meta programming——元编程。

其实网络上也能搜出很多相关的文章,对于该词的定义参考wikipedia的一句话:

Metaprogramming is a programming technique in which computer programs have the abILity to treat other programs as their data. It means that a program can be designed to read, generate, analyze or transform other programs, and even modify itself while running. In some cases, this allows programmers to minimize the number of lines of code to express a solution, in turn reducing development time. It also allows programs greater flexibility to efficiently handle new situations without recompilation.

简而言之,就是将程序作为数据,可以用于操作程序或者自身,而实现某些能力,比如将运行时工作移到编译时。

按照编译器发展的进程,元编程可实现如代码替换(通过宏实现),泛型编程(从宏演变而来,脱离类型,高度抽象逻辑,可减少代码量),或者在更高级的语言中,运行时通过内省/反射机制来操作代码逻辑,或者随着编译过程的解耦和开放,可以实现在中间语言阶段(AST,IL),操作语法树和中间语言,实现更可扩展性的能力。

Dart做为一门现代高级语言,除了模板化能力,也能基于中间语言来操作代码。本篇文章主要讨论如何基于其中间语言(dill),通过AST树的操作来进行元编程,并实现一些现有dart语法本身实现不了的能力。并且这种实现在编译时,对于程序在运行时的性能几乎没有影响。

2. Dart中的元编程简介

2.1 背景知识

我们知道,几乎任何语言中,代码在 "编译"(解释型语言在运行时也有编译的过程) 的过程中,都会生成一种树状的中间状态,这就是 AST(抽象语法树)。AST 描述了每个表达式/语句中的子语句的执行顺序和执行逻辑,因而它可以被很方便地翻译成目标代码 。基于这种抽象,能合理的将编译器拆分为三阶段:FrontEnd,Optimizer, Backend,从而实现能兼容各种语法形式的语言,更易于迁移并兼容不同架构的cpu。见下图:

这三个阶段围绕这IL(intermediate language)进行。IL语言隔离了语法(能轻易适配各种新的语种),平台架构等的差异性。

2.2 Dart的编译流程

Dart的设计也类似,其中间语言就是Dill。不同的是,这里的Dill不像java的IL或者DotNet的IL那样开放出来可以直接编写,而是通过程序的方式操作实现。

这种方式其实就是基于AST库对Dill进行manipulation。

这个库内的组件包含了所有AST树涉及到的节点的定义和访问,将类型,函数,语句,声明,表达式等编程基本概念抽象成了对象。基于这些对象我们可以遍历整个AST树, 或者生成新的类型和函数,插入代码语句,实现新的逻辑。

2.3 几个栗子

入门其实很简单,看一下例子代码就可以啦。

2.3.1. 比如以下语句定义了一个新的Map变量,并且调用了它的构造函数:

//组装参数Arguments mapFromArgs = Arguments.empty();mapFromArgs.positional.add(MapLiteral([], keyType:keyInterType));//调用from构造函数StaticInvocation mapConstructor = StaticInvocation(MapFromFactoryProc, mapFromArgs);//声明一个名字为jsonMap的Map类型变量VariableDeclaration mapInstDecl = VariableDeclaration("jsonMap", type:mapInterType);//相当于var jsonMap = new Map();VariableSet set_mymap = VariableSet(mapInstDecl, mapConstructor);

2.3.2. 创建函数体

函数体其实就是Block。

Block bodyStatements = Block(List<Statement>());bodyStatements.addStatement(mapInstDecl);bodyStatements.addStatement(ExpressionStatement(inst));

2.3.3 创建函数

这个例子是参考某个函数的声明形式来创建新函数,篇幅所限,一些参数从略。

static Procedure createProcedure(Procedure referProcedure ,Statement bodyStatements, DartType returnType) { FunctionNode functionNode = new FunctionNode(bodyStatements, //...参数从略 ); Procedure procedure = new Procedure( Name(referProcedure.canonicalName.name, referProcedure.name.library),ProcedureKind.Method, functionNode, //...参数从略 ); return procedure; }//调用函数创建,并添加到类定义中Procedure overridedToJsonFunc = createProcedure(JsonTypes.StaticBaseToJsonProc, bodyStatements, InterfaceType(mapClass));youClazz.addMember(overridedToJsonFunc);

2.3.4 其他

基于AST还可以创建复杂的表达式和语句,如ForInStatement(for...in循环)等,语句和表达式还可以通过ExpressionStatement和BlockExpression互相转化。更多的技巧可参考AST的定义。

2.4 如何调试

编辑好的Dill似乎是个黑盒,除了看日志或者看异常堆栈,并不能进行单步调试,给开发带来了一些困难。但Dart提供了已将将kernel dill转成可阅读的文本的工具,方便调试:

$DartHome/dart ../../pkg/vm/bin/dump_kernel.dart /your/dill/file/path /output/dill/text/file.text

打开的text文件是类似于这样的:

static method __from_Json1(core::Map<dynamic, dynamic> m) → AOP2::UserDataT { aop2::UserDataT inst; inst = new aop2::UserDataT::•(); inst.name = m.[]("name") is core::String ?{core::String} m.[]("name") : null; inst.city = m.[]("city") is core::String ?{core::String} m.[]("city") : null; inst.age = m.[]("age") is core::int ?{core::int} m.[]("age") : null; inst.squres = m.[]("squres") is core::double ?{core::double} m.[]("squres") : null; inst.sex = m.[]("sex") is core::bool ?{core::bool} m.[]("sex") : null; inst.houses = m.[]("houses") is core::Map<dynamic, dynamic> ?{core::Map<dynamic, dynamic>} block { core::Map<core::String, core::String> mymap; mymap = col::LinkedHashMap::from<core::String, core::String>(<core::String, core::String>{}); for (core::String item in (m.[]("houses") as core::Map<dynamic, dynamic>).keys) { mymap.[]=(item, (m.[]("houses") as core::Map<dynamic, dynamic>).[](item) is core::String ?{core::String} (m.[]("houses") as core::Map<dynamic, dynamic>).[](item) : null); } } =>mymap : null; return inst; }

3. 应用畅想

基于Dill的Manipulation,我们可以实现往代码中注入新的逻辑。比如闲鱼科技之前开源的AOP库AspectD的原理就是通过加载dill文件生成AST,然后遍历AST,寻找到已经annotation到的函数或语句,在dill层面操作后又生成dill参加到编译器后续的流程,最终实现了AOP。

类似的,我们知道Dart对于Json解析操作不是很方便,jsonDecode不能直接生成业务对象,而是Map或者List之类的集合,还需要用户自己手动代码遍历这些集合并装载对象。虽然官方开源了一个基于Source_gen的方案,但使用上也不友好(还有其他一些方案如Dason等,但依赖于Mirror,详见这里的比较)。其实遍历Map或者List并装配对象这样的操作逻辑很简单,我们也可以通过Dill Manipulation来做。

其使用方式简便,举例如下:

@JsonModel()class UserData { String name; String city; UserData son;}void main(){ var str = ''' { "name": "Jim", "city": "hangzhou", "son":{ "name": "Kong", "city":"Hangzhou" } } '''; UserData userData = JsonDrill.fromJson<UserData>(jsonDecode(str)); var map = JsonDrill.toJson<UserData>(userData); print("$map"); }

更深入的思考一下,Dart现有的mirror能力至今未推荐使用(原因分析可参考这篇文章),那我们是否可以基于Dill Manipulation实现一个简单轻量的LiteMirror库呢?并基于这个LiteMirror库实现更上层的Json解析和AOP甚至Hook能力?

当然,聪明的你或许已经发现,Dill Manipulation不可避免的要对编译流程进行定制,这就要求比如在Flutter环境中,需要对Flutter Tool进行定制,以加入Dill再编辑的环节。剧透一下,闲鱼科技目前就已经实现了Json解析器,正在准备开源中,敬请期待

作者:余玠

,
    推荐阅读
  • 笑话句子(笑话句子精选)

    女人装比那叫资本,男人装比那叫变态。两只鸳鸯同命鸟,一对蝴蝶可怜虫。每当你错过了这段感情,很难挽回。自从买了保险,我走路都不看红绿灯了。待我长发及腰,我定要去烫个大波浪卷。电脑再高明,也是人脑发明出来的。整容医生一看到我,就知道大生意来了。当小三转正后,就失去了小三有的魅力。余生那么美好,何必和一群狗打交道。人不能低下高贵的头,但捡钱时例外。我爱的起就输的起,何况我还很年轻。

  • 分享网站的著作权侵权问题(第一弹App因侵犯著作权被罚130万元)

    分享网站的著作权侵权问题蓝鲸TMT频道5月25日讯,2020年6月,静安区检察院对包括第一弹App创始人王某在内的犯罪嫌疑人进行依法逮捕,因平台擅自上传大量美日韩等国的影视内容,并从中收取会员充值费及广告费用,合计非法获利3418万余元。此外,公司其余26人也被判处有期徒刑三年缓刑三年,并处罚金人民币30万元到有期徒刑九个月缓刑一年,并处罚金人民币2000元不等的刑罚,还有最后1人将择日宣判。

  • 盈余一词中盈的意思是什么(盈余的解释)

    下面内容希望能帮助到你,我们来一起看看吧!盈余一词中盈的意思是什么盈余一词中盈的意思是增加、增长的意思。盈余,拼音是yíngyú,表示有富余,有剩余,出于《后汉书·桓鸾传》。盈余的基本解释:有余,多余。收入款项对于支出款项的超过额预算盈余。

  • 爱国名句(盘点爱国名句)

    我爱我的祖国,爱我的人民,离开了它,离开了他们,我就无法生存,更无法写作——巴金,今天小编就来说说关于爱国名句?——巴金爱国心再和对敌人的仇恨用乘法乘起来——奥斯特洛夫斯基爱祖国,首先要了解祖国;不了解,就说不上爱。这就是说,要渴望祖国能成为人类理想的体现,并尽自己的力量来促进这一点。王师北定中原日,家祭无忘告乃翁。——柏拉图国家用人,当以德为本,才艺为末。

  • 核心交换机 防火墙功能(路由器和核心交换机之间的关系是什么)

    正文在智能化弱电项目组网中,防火墙,路由器和核心交换机所处的层级不同对应的功能也不同,但3种设备也有部分功能重叠。特殊行业使用路由器作为出口总结防火墙,路由器,核心交换机,这几个产品都是网络中的重要组成设备,特别是防火墙和路由器彼此都有很多类似的功能,在一些中小企业组网中会直接选择防火墙作为出口,省去路由器产品,但大部分的企事业单位都会选择路由器作为出口,需要实现对应的业务。

  • 关于女人的经典语句(女人的经典语句有什么)

    跟着小编一起来看一看吧!关于女人的经典语句事业是男人的姿色,姿色是一个女人的事业。真正的才华如火焰般难以收藏,总会燎原。男人爱你的程度,取决于你爱自己的程度。你要留住老公的心,首先要留住老公的眼球。活在这个世上,被人需要,是最极致的幸福!中年一个女人更需要好心情,学会调节放松最重要。和对自己有恶意的人绝交。人有绝交,才有至交。每天打扮的优雅从容出门,给自己带上不同的笑容。

  • 六盘水社保转移指南 六盘水市社保中心

    六盘水社保转移是需要个人办理的手续,六盘水社保转移仅包括:养老保险、医疗保险和工商保险的转移,其中省内转移可以直接在社保局办理,跨省转移手续相对复杂。

  • 合资suv自吸加at变速箱(SUV还是自吸发动机配AT变速箱耐用)

    17英寸的轮毂尺寸不算大,但配在这辆小型SUV车上,刚好合适。时速超过100公里,动力才开始渐渐减弱。车头配备了盘子形进气格栅,并与两侧大灯无界限连接,辨识度较高。后排配有座椅比例放倒功能,实用性可以。该车实测百公里综合路况油耗是9.5升。身高1.78米的乘客坐进前排,将座椅调至最低,头部空间四指。保养费用,行驶5000公里/6个月,需要更换发动机机油和机油滤清器,费用是612元。

  • 三亚景区学生票(毕业季优惠套餐)

    各旅游景区在融合创新上下功夫,推出多种新业态,提供丰富多彩的节日创意活动,有效增强景区的吸引力,提升游客的重游率。为提升旅游消费吸引力,中免三亚国际免税城、三亚海旅免税城、中服三亚免税购物公园也持续推出优惠促销活动,为消费者带来免税购物的专享福利。5月20日,首届“心动三亚”城市旅游节活动即将启动,届时将举行“心动三亚”鹿城夜跑活动、“520心动集市”、无人机表演及其他丰富多彩的活动。

  • 白癜风治疗最好的偏方(白癜风的治疗方法汇总)

    白癜风是一种后天性获得性色素缺乏症,多伴有黑素细胞的破坏。白癜风病程长,治疗缓慢,尤其是长在暴露部位,给患者造成很重的心理、经济负担。而且目前各种私立的所谓白癜风专科医院、各种小广告、偏方随处可见,往往到最后受伤、受骗的还是患者。主要用于面积较大的进展期白癜风。火针第四、手术治疗该类方法仅适合稳定期白癜风,进展期不可以使用。