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

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

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

本篇文章主要讨论如何基于其中间语言,通过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解析器,正在准备开源中,敬请期待

作者:余玠

,
    推荐阅读
  • 身高与遗传因素的关系(身高是遗传爸爸还是妈妈)

    长高10厘米和给你10万元选哪个?很多人做梦都希望自己再高一点,那么,成年后还能长高吗?今天,我们就来解答关于身高的各种疑惑。

  • 韵达快递没有物流信息怎么查询(想要将大量韵达快递的每个物流信息都查询出来怎么操作比较快)

    对于每天都发出大量快递的小伙伴来说,拥有一款能够批量查询、管理快递单号的软件是很刚需的,那今天就给大家分享一下这么一款软件。

  • 傣族服饰特点简介(傣族服饰特点是什么)

    傣族服饰特点简介傣族无论男女,出门总喜欢在肩上挎一个用织绵做成的挎包(筒帕)。傣族妇女均爱留长发,束于头顶,有的以梳子或鲜花为饰,有的包头巾,有的戴高筒形帽,有的戴一顶大斗笠,各呈其秀,各显其美,颇为别致。德宏地区傣族妇女的发式也不一样,年轻的妇女是将头发束于头顶,中老年妇女则不再束发,而改戴一种黑色的高筒帽。

  • 有关于抗美援朝的资料(抗美援朝的意义是什么)

    1950年7月10日,“中国人民反对美国侵略台湾朝鲜运动委员会”成立,抗美援朝运动自此开始。1953年7月,双方签订《朝鲜停战协定》,从此抗美援朝胜利结束。10月25日为抗美援朝纪念日。2020年7月2日报道,2020年是中国人民志愿军抗美援朝出国作战70周年,将以中共中央、国务院、中央军委名义颁发“中国人民志愿军抗美援朝出国作战70周年”纪念章。

  • 翡翠玉佩多少钱(翡翠玉佩多少钱一个)

    同样大小的玉石,根据玉石的不同种类、不同质地,价格有可能是几十元,也有可能是几十万元。

  • 山东大学最新特点(一校三地八校区)

    逐步的,这些分校区逐渐成为伯克利平级的、独立的大学。山东境内最好的当然是山东大学,于是说服山东大学去威海建了个分校。在全国来说,青岛的名声要大于济南,更容易吸引老师和学生。济南6校区山东大学除了威海、青岛2个校区之外,在济南还有六个校区,可以称美丽的济南坐落在山大里面。建设了章丘校区,山大会置换出兴隆山校区,并退还软件园校区,其他校区保留,相当于在济南有5个校区。

  • 开门见到鱼缸好吗(为什么开门不能见鱼缸)

    很多人为了美观,会选择在客厅装一个鱼缸。不过,鱼缸的摆放也是有学问的,就是鱼缸的摆放不能正对着大门。所以水具有止衰气、聚旺气的双重作用。一般的鱼缸都不宜过于高大,水过高则为灾,鱼缸的水面最好不要高于人的颈部。但是一旦鱼缸的摆放位置不恰当,就可能会造成挡煞之灾。所以,我们在装修时一定要注意,鱼缸的摆放位置需要契合风水原理,这样做才能让客厅财运起到事半功倍的作用。

  • 我爱你的句子浪漫(真的好爱你的感动句子)

    以下内容希望对你有帮助!我不会让任何人伤害你。这是我的许诺,寂静无声。你无须知晓,它只在我心。我要让全世界都降温,来降低我对你爱的热度。你塞满了我的整个过去,却在我的未来永久缺席。我对你的爱是刀烧酒,热情浓烈。如果我是一只鱼,我迷恋海洋,如果我是一只鸟,我迷恋天空。可我是红尘中的一个人,我只迷恋你!

  • 爸爸我想对你说内容(需要写到什么)

    下面更多详细答案一起来看看吧!爸爸我想对你说内容爸爸,你什么都好,就是抽烟的坏毛病改不掉。爸爸,请你把烟戒了吧!记得爷爷住院那几个星期,你一天就抽一盒多得烟,因为你心里很难受,主要爷爷得的病是脑血栓。爸爸,你跟我们说过多少次要戒烟,但你的实际行动却没做到,瞧你现在面黄肌瘦,显得很憔悴。所以,我和爷爷、妈妈强烈要求你的坏毛病改掉,成为一个健康快乐的好爸爸。

  • 联想笔记本有摄像头找不到怎么办(联想笔记本摄像头怎么打开)

    联想笔记本有摄像头找不到怎么办?联想笔记本是国产笔记本大厂,很多用户的笔记本电脑都是联想的,因此很多用户都在问:联想笔记本摄像头怎么打开?今天小编就来给大家分享一下联想笔记本摄像头怎么打开,今天小编就来聊一聊关于联想笔记本有摄像头找不到怎么办?