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

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

作者:余玠

,
    推荐阅读
  • 黑西装外套搭配什么内搭好(黑西装外套搭配内搭的技巧)

    黑色总给人一种神秘感,冷酷的造型让人有种不敢靠近的感觉,但却非常的有女人味。下身再搭配一款今年非常流行的亮色运动裤的话,给人又是另外一种感觉,瞬间打破了原有的严肃以及沉闷的感觉,为整体的造型注入了些许年轻的味道。所以也是妹子们值得一试的穿搭方式哦。搭配灰色一款好看的灰色打底衣,搭配上一条浅色的灰色运动裤,全身呈现出一个黑灰的颜色组合,打造出来的造型十分有层次感哦。

  • 各个国家度量衡来源(盘点旧度量衡具)

    明代以后,尺寸已接近现代的标准,但却出现了三种:其一是裁衣尺,1尺等于现代的34厘米;其二是量地尺,1尺等于现代的32.7厘米;其三是营造尺,1尺等于现代的31.1厘米。清代时,尺寸标准又有变化,裁衣尺等于现代35.5厘米,量地尺等于34.5厘米,营造尺等于32厘米。到了清代,钧早已消失,衡制有石、斤、两、钱、分,1石等于120斤,1斤等于16两,1两等于10钱,1钱等于10分。此衡制一直沿至现代。

  • 生地黄的功效与作用及禁忌 生地黄的功效与作用及禁忌症有哪些?

    上面详细为大家介绍了生地黄的功效与作用,也让大家了解它的服用禁忌,能让大家在需要时及时正确服用生地黄,也能让生地黄发挥更好的养生功效。

  • 韩式拌饭怎么做好吃(韩式拌饭做法)

    韩式拌饭怎么做好吃食材:西葫芦85g、胡萝卜45g、香菇60g、菠菜100g、豆芽100g、午餐肉20g、鸡蛋100g、米饭400g、雪碧20g、韩式辣酱35g、蕃茄酱20g、香油10g、白芝麻10g、盐5g。将胡萝卜、西葫芦切丝,午餐肉和香菇切片。锅中加入少许油,将胡萝卜丝、西葫芦丝、香菇分别煸炒。午餐肉煎至两面金黄。再煎一个荷包蛋,全熟还是溏心可自己选择。调制酱汁:2勺韩式辣酱、3勺雪碧、1勺番茄酱、1勺香油、1勺白芝麻、少许盐,以上搅拌均匀。

  • 香蕉去皮可以冷冻保存吗(香蕉去皮可不可以冷冻保存)

    香蕉果实含淀粉0.5%,蛋白质1.3%,脂肪0.6%,糖分11%,灰分1%,维生素A、维生素B、维生素C、维生素E等。并含少量5-羟色胺、去甲肾上腺素和二羟基苯乙胺。香蕉属高热量水果,据分析每100克果肉的发热量达91大卡。在一些热带地区香蕉还作为主要粮食。其中维生素A能促进生长,增强对疾病的抵抗力,是维持正常的生殖力和视力所必需;硫胺素能抗脚气病,促进食欲、助消化,保护神经系统;核黄素能促进人体正常生长和发育。

  • 长时间压力大怎么缓解(疲乏无力还浑身难受)

    ===PEI_PAGER_PEI===3、快乐婴儿式取仰卧位,双臂放于体侧,抬起双腿;用双手抓住脚底,双腿微曲,尽量将膝盖拉向胸部,有助于打开髋部。取仰卧位,双膝弯曲,双脚平放于地面;右脚放在左膝上,左腿弯曲,成4字;双手在左膝后方相握,将左腿拉向胸部;另一侧做同样动作。

  • 海星的营养价值及功效(有什么需要注意的吗)

    以下内容大家不妨参考一二希望能帮到您!海星的营养价值及功效海星中的酸性粘多糖有调节免疫功能作用。海星可以补充丰富的维生素胶原蛋白,可以有强身健体的作用,对于强身健体,提高人体免疫功能有好处。近年来,海星的药用价值逐步被重视,不少海洋药物或食品企业开发了海星营养素胶囊等产品,对祛病强身有显著的功效。海星的体内有毒素,所以在食用的时候一定要注意,不能盲目的食用,一定要煮熟煮透以后食用。

  • 宝马e38参数 宝马e38参数配置

    宝马e38参数宝马e38是1994年至2001年生产的宝马7系,到2019年10月已经完全停产。截至2019年10月,宝马e38已经完全停产,二手价格在2万元左右。虽然2万元就能买到,但维修费用可能高达3万元。一般来说,像宝马e38这样的车属于感伤型车,车主购买后一般不会作为交通工具使用,而是收藏。目前carplay只支持苹果手机,安卓手机无法使用这个功能。

  • 京东商城小天鹅冰柜(小天鹅冰柜价格推荐)

    跟着小编一起来看一看吧!京东商城小天鹅冰柜在这个时代,经济和技术都非常发达。经济的改善给人们的生活带来了巨大的好处。冰柜已经进入人们的生活。夏天人们也可以吃新鲜的蔬菜,那么什么牌子的冰箱好呢?以下编辑将介绍LittleSwanFreezer的价格建议。有关更多信息,您可以关注土巴兔网资讯。

  • 烤箱怎么烤猪皮(烤猪皮的做法)

    我们一起去了解并探讨一下这个问题吧!烤箱怎么烤猪皮食材:猪皮236克,八角2克,酱油15克,料酒8克,香叶2克,烧烤酱2克,花椒粉2克,辣椒粉2克。猪皮洗净,放入冷水里,加入两勺酱油,两勺料酒,适量花椒大料香叶。将煮熟得猪皮捞出,清理掉上面的肥肉部分。然后用水洗两遍。切成小块,穿起来。均匀的刷上酱汁。准备花椒粉,孜然粉,辣椒粉,白芝麻,混合。

热门推荐