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

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

作者:余玠

,
    推荐阅读
  • 笔记本电脑长时间充电有什么危害(笔记本一直充电究竟有没有副作用)

    似乎在骨子里,国内消费者都将保护终端设备、延长产品使用寿命等当成了一项头等大事来抓。在连接电源并充满电后,就会自动保护电池,采用一定的机制让笔记本电脑处于正常使用状态。因此,最好的保养方法,就是将笔记本电脑连接电源且不要断开。一旦经常只使用电池工作,必然会损耗充电周期的次数,反而会对电池寿命造成不利影响。为了延长电池寿命而为自己制定各种条条框框,反而丢掉了产品为我们服务的初衷。

  • 怎么看手机号码用了多久(如何看手机号码用了多久)

    以中国移动手机号码为例,打开中国移动网上营业厅主页面,点击顶部“登录”按钮;,我来为大家讲解一下关于怎么看手机号码用了多久?跟着小编一起来看一看吧!

  • 树叶贴画手工秋天主题带文字(专属秋天的手工DIY秋天树叶贴画)

    要说秋天最多的是什么,除了丰收的硕果,就是五彩缤纷的落叶了!落叶常被人们视作离愁别绪,但其实它也有很多花样玩法,不论是大人还是小孩,都可以畅玩一通,让你瞬间容身大自然!但在挑选树叶上,尽量不要选择脆性大的,避免粘贴过程中,树叶破损,影响美观。由于操作的简单性,更适合3-5岁的小朋友,用于智力与思维模式的开启,并对其空间想象力、创造力等都会有一定的提升作用。还可以直接利用树叶当画纸,在树叶上作画。

  • 珍珠粉和什么做面膜最好(面膜功效)

    接下来我们就一起去研究一下吧!自制及使用方法:将鸡蛋敲碎析出蛋清,加入珍珠粉,搅拌均匀备用。洁面后,均与涂于脸上,尽量涂厚一点,不然会很快干掉,保持15-20分钟后用清水洗净即可。

  • 怎么用小麦粉做吃的(小麦粉做吃的应该怎么做)

    把1倒入2的中间,用筷子对混合物进行搅拌,搅成片状后,倒于光滑的工作台面上。把光滑的面团在工作台上用料理碗盖好,静置发酵至2.5倍大。发好的面团,用擀面棒进行排气。工作台上放一小碟面粉,每次取一个小面团在面粉上滚一下,沾上轻薄的面粉后,用擀面棒擀开成中间厚周边薄的圆形面片。包好的包子排在垫了湿油布的蒸架上,包子之间要留出与包子半径相等的空间,以防止发酵膨胀后包子被粘连。包子排好后,盖温布进行二发。

  • 毁灭国家机关印章罪怎么判? 使用国家机关印章罪

    对毁灭国家机关印章罪的量刑标准可能是拘役、管制或三年以下有期徒刑,也有可能在三年以上到十年以下有期徒刑间量刑,并且《刑法》明确规定,涉嫌毁灭国家机关印章罪,的可以并处罚金,但没有明确规定罚金标准,量刑标准主要跟犯罪情节有关。

  • 上海崇明建筑微旅行线路推荐(上海崇明旅游)

    崇明区01古韵悠悠,东海瀛洲类型:红色文化、江南文化线路长度:5预计游览时间:1天涉及建筑崇明岛岛碑、崇明区博物馆、寒山寺、瀛洲公园、金鳖山、寿安寺线路亮点崇明学宫是人文荟萃之地,展示崇明千年绝代风华。瀛洲公园园林精致,林木繁盛,静水潺潺。寿安寺殿堂雄伟,佛像庄严,既有深山古刹之神韵,又具平川名寺之风华。在白墙黑瓦间走走停停,感受独有的崇明味道。

  • 陕西历史博物馆的特色体验(打卡陕西9家国家一级博物馆)

    1陕西历史博物馆陕西历史博物馆简称陕博,位于西安市雁塔区小寨东路91号,是中国第一座大型现代化国家级博物馆,也是国家4A级旅游景区。从开馆至今,秦始皇陵兵马俑博物馆已接待海内外观众达8000多万人次,其中外国国家元首、政府首脑等就有将近200人次。西安博物院是第二批国家一级博物馆,同时也是一处国家4A级旅游景区。博物馆建筑面积3.48万平方米,展厅面积1.1万平方米,拥有各类文物48万余件,其中珍贵文物3245件。

  • 经济学十大基本假设(经济学十大基本原理第二位)

    中国选择全民抗疫,全国经济活动基本停止,经济受到一定的影响。其他许多国家,选择经济优先,不限制经济活动,导致病毒快速蔓延。为什么会出现不同的选择,原因恐怕就在于计算机会成本的方式不同,中国更在乎人民的生命健康,至于外国领导人更在乎的是经济还是选票,就不是我们能说清楚了。

  • 肉苁蓉的种植方法与技术(有机肉苁蓉种植规程)

    梭梭种子要求头年采集,翌年育苗,不能隔年。9)种子处理:用0.1%~0.3%的高锰酸钾或硫酸铜溶液浸种20~30分钟后捞出晒干。发病严重时,全枝被白粉囊盖,甚至引起枝条枯死。立柱用角钢或水泥桩,水泥桩规格采用120×120×1800cm;中立柱、角柱用10mm50型角钢;网片横线7层,110cm宽,竖线间距40cm。设置立柱间距为10cm;中立柱间距400m。20)接种:将制作好的接种盘覆沙土后置于接种穴内梭梭根系分布密集处。