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

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

作者:余玠

,
    推荐阅读
  • 朗逸和新宝来哪个更值得购买(一文看懂朗逸和宝来的历史)

    朗逸和宝来是国内热度相当高的两款紧凑级轿车。2022年2月,朗逸以26707辆的销售成绩排在了轿车销量榜第二位。朗逸的最低配车型是2022款朗逸1.5L手动风尚版,官方指导价在11.29万。朗逸2022款1.5L自动视野版,官方指导价在13.69万,宝来2021款1.5L自动舒适智联版,官方指导价13.60万。中配版的宝来增至到了6气囊,数量上和朗逸保持了一致。不过宝来的皮质座椅具有两向腰部支撑。至于朗逸和宝来的1.4T车型,在这里是不做推荐的。

  • 宝宝止咳化痰拍背手法(揉天突帮助宝宝快速止咳)

    天突穴又称玉户,直白一点说就是我们的胸腔开在外面的一个“烟囱”,是气机出入的通道。此穴能通利肺气,使之爽利通畅。我们说呼吸靠的是肺,天突穴和呼吸密切相关,所以治疗肺的疾病当然也离不开它。咳嗽的基本病机是肺的气机升降出现了失调的状况,肺气上逆,引发咳嗽。怎样用天突穴给孩子快速止咳呢?主治孩子咳嗽、气喘、胸痛、咽喉肿痛、打嗝等。

  • 滚筒洗衣机洗一次多久 滚筒洗衣机洗一次多久时间

    滚筒洗衣机洗一次一般要1个多小时。滚筒洗衣机发源于欧洲,洗衣方法是模仿棒锤击打衣物原理设计。滚筒洗衣机是由不锈钢内桶,机械程序控制器,经过磷化、电泳、喷涂三重保护的外壳,和若干笨重的水泥块用于平衡滚筒旋转时产生的巨大离心力做重复运动,加上洗衣粉和水的共同作用使衣物洗涤干净。因为用料主要以钢铁等做成,所以寿命一般在10至20年左右。

  • 关于竹子的古诗有哪些(关于竹子的古诗汇总)

    近处竹林围绕的屋舍边,有长满衰草的小池塘,蝉鸣缭乱。竹子可织成竹席送给美丽的姑娘纳凉;也可以裁制成钓竿,钓上大鱼几筐。开门复动竹,疑是故人来。微风吹开院门又吹动了竹丛,让人怀疑是旧日的朋友到来。

  • 用什么东西能快速消除水垢(去除水垢不再难)

    倒掉山芋,就能发现水垢会逐渐脱落!这时,千万不能擦洗内壁,不然就会失去除垢作用。煮沸后,放置半小时左右,再用清水洗刷。

  • 内卷为什么在现在流行(还有很多人不懂什么是)

    自2020年以来,“内卷”一词一直在主流知识界流传。事实上,内卷是我们当前生活方式的绝大多数。因此,996成为标准。然后我们可以定义内卷,也就是说,在某种游戏规则中,每个人都被迫四处争斗,无论我们多么努力,无论我们付出多少,我们都无法改变我们的命运。此时,我们很容易发现内卷群或我们自己有一些共同的特征。

  • 犯罪凶手讲述他和女友的悲惨经历(为破案和凶手上床)

    协助警方调查的侧写师给出了凶手的形象。这也是上级找莉兹的主要目的,接近科林,找证据,给他定罪。科林被捕后,新闻媒体大肆报道,人民群众拍手叫好,莉兹也因为卧底有功终于得到了嘉奖。一年的羁押期后,科林被当庭无罪释放。因为法官认为这种“引诱”的行为有钓鱼执法的嫌疑,且科林被捕之后拒不认罪。而科林在被无罪释放之后,申请了国家赔偿70万英镑,交了稳定的女友,反而生活走上了正轨。

  • 2022外地来南京最新防疫规定(外地来南京疫情防控要求)

    端午假期来临,倡导南京市民端午期间非必要不离宁,确需离宁的务必向目的地报备,具体的出行政策见正文。

  • 巴西龙骨的功效与作用及药用价值(巴西龙骨的功效与作用及药用价值及禁忌)

    2、胃病腹泻巴西龙骨入药还能治疗人类的胃病和腹泻,治疗的时候需要取新鲜的巴西龙骨二两,洗净以后切成片,再与新鲜的猪肚放在一起炖汤喝,这样能让人们的胃炎,胃痛很快好转,如果人们出现腹泻的症状,则可以直接把新鲜的巴西龙骨加清水煎制以后服用。

  • 自学考试性价比最高的专业(自学考试通过率较高的专业有哪些)

    商务英语马克思主义基本原理概论、中国近现代史纲要、BEC商务英语、国际贸易实务、国际市场营销学、商法、商务英语沟通、国际商务谈判、商务英语口译、国际贸易结算、国际商务虚拟运行等。很多人心存侥幸,不断降低对自己的要求是考生最大的弊病。