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

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解析器,正在准备开源中,敬请期待

作者:余玠

,
    推荐阅读
  • 最新牛年温馨祝福语(适合牛年祝福语的句子)

    最新牛年温馨祝福语祝愿您身体发福,精神发福,事业发福,爱情也发福。新的一年即将来临,在此祝愿各位领导新春愉快、工作顺利!新的一年里事事顺心,学业有成,学校创造新的辉煌。2021,愿你手边多银财,方寸永不乱。愿你春节福禄安康,2021牛年大吉大利,事业辉煌。继往开来迎新岁,举金杯春满万户,与时俱进贺丰年,传笑语喜盈千家。牛年佳节好运到,福禄寿星进门来!竹报三多,红梅报喜,瑞雪迎春,阳春召我,淑气宜人。

  • 海白菜可以冷冻保存吗(海白菜相关介绍)

    海白菜可以冷冻保存吗?下面希望有你要的答案,我们一起来看看吧!海白菜的主要成分是水分,水分和胶质融合,形成独特的口感,可以短时间的冷冻,但是长时间冷冻的话,水分会凝结成大冰晶,导致海白菜的组织纤维受损,出现口感上的改变,需要注意的是,海白菜还冷冻的时候要选择速冻的方式。

  • 咖喱粉是用什么做成的(咖喱粉是啥制成的)

    黑胡椒粉是用带皮胡椒果实研磨制成的,白胡椒粉则是先将胡椒果实的黑色外皮去除后,研磨而成的。可是提起咖喱粉,很多青年厨工不知所以,甚至有人认为它是用咖喱树的果实制成的,其实不然。咖喱粉是用多种香料按一定的比例配制成的调味品,使用较广,西餐、中餐都有用场。用咖喱粉调制的菜肴,色、香、味等方面都富有特色。如咖喱牛肉、咖喱鸡块等都是脍炙人口的菜肴。将上述原料混合后粉碎,再搅拌均匀,即成为咖喱粉。

  • 滚筒洗衣机可以用洗衣凝珠吗 滚筒洗衣机可以用洗衣凝珠吗?

    气味芬芳,质地软滑浓稠适中,性质温和不伤衣物,特有的低泡浓缩锁色配方,更易漂洗,凝珠遇水即溶无残留,还能有效快速去除顽固污渍让衣物洁净如新。

  • 行测近义词有哪些(公考近义词辨析)

    现多比喻做事时因力不胜任或没有条件而感到无可奈何。侧重强调因能力、条件不足而没有办法。望尘莫及VS望其项背望尘莫及释义:只望见走在前面的人带起的尘土而追赶不上。现在多用于表示对人钦佩的自谦语。第一空,根据文意,企业们因为政策没有落地有不敢投资之意,D项“望尘莫及”,比喻远远落后,与题意不符,排除。故正确答案为C。

  • 季度工作总结范文(个人季度工作总结范文)

    加强客户营销,增加客户群体只有不断开发优质客户,扩展业务,增加存款,才能提高效益。

  • 广州白云区幼儿园招生时间及安排2023 2021年广州白云区幼儿园招生

    (二)参加区属教育部门办公办园电脑派位的,由幼儿父(母)或其他法定监护人在规定时间内登陆指定网站进行报名。其他幼儿园具体报名办法由幼儿园及其举办单位严格按照疫情防控工作要求自行制定,并向社会公布后实施。符合政策性照顾类别的适龄儿童,根据广州市义务教育阶段政策性照顾学生清单等要求提供相关证明材料。如在资格审核或入学注册中发现身份信息不符或弄虚作假等情形,将取消录取资格。

  • 家里不养的四种花(2种花别养屋里头)

    彼岸花花色鲜红花朵较大,一般在山上山坡后面会长着一丛丛的彼岸花,家里的老人总是不让摘这些花,说它是死亡花,是陪伴死人的花,拿回家来不吉利。有些朋友偏偏不信,在家里养上很多彼岸花,秋天开花时火红的一片也非常漂亮。不过这种彼岸花也叫石蒜,如果家里有小孩和宠物千万不要误食,否则容易中毒,真的变成不吉利的花了。

  • 火锅店生意惨淡美女想出经营奇招(做生意的经营之道)

    火锅店生意惨淡美女想出经营奇招?

  • 怀孕中期腰酸腿疼怎么回事(怀孕后腰酸腿疼又抽筋)

    “老公老公,我腿抽筋儿了,快帮帮按按,啊,疼死了!”缺钙还容易引起肌肉痉挛,就是“抽筋儿”。很多孕妈妈觉的疼就疼了,我能忍,怀孕了,不想吃太多药。作用远不止于此。你的全身小血管都在痉挛,尤其是盆腔血管,血管痉挛,自然血流量减少,子宫供血相对减少,就意味着宝宝得不到足够的营养,宝宝的发育就有可能迟缓。不需要夸张的加大某种食物的比例,饭还是要吃的开心最重要,好心情对宝宝又何尝不是一种“营养”呢!