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

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

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

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

作者:余玠

,
    推荐阅读
  • 卡戴珊什么梗(网络语卡戴珊什么梗)

    卡戴珊什么梗卡戴珊是美国“名媛”,其家族长年混迹娱乐圈和体育圈边缘,靠着“真人秀”炒作维持热度。卡戴珊因为夸张的身材比例经常被调侃甚至“玩梗”。在休赛期各种娱乐新闻、体育新闻跟踪报道,他们和女友卡戴珊沉迷玩乐、纵欲过度、耽于训练,赛季开始表现不好,球迷和媒体就开始指责卡戴珊,NBA毒瘤的名号就这样背上了。

  • 薅羊毛是什么意思(薅羊毛的含义)

    下面更多详细答案一起来看看吧!薅羊毛是什么意思所谓薅羊毛就是指网赚一族利用各种网络金融产品或红包活动推广下线抽成赚钱,又泛指搜集各个银行等金融机构及各类商家的优惠信息,以此实现盈利的目的。这类行为就被称之为薅羊毛。就目前来说,“薅羊毛”的定义已经不仅仅局限于互联网金融领域,已经渗透到社会各个领域,外卖优惠券、减免优惠、送话费、送流量等诸多活动,都可以称之为薅羊毛。

  • 河池交通违章缴费期限 河池交管所

    根据2004年5月1日起施行的中华人民共和国道路交通安全法:第一百零八条当事人应当自收到罚款的行政处罚决定书之日起十五日内,到指定的银行缴纳罚款。对行人、乘车人和非机动车驾驶人的罚款,当事人无异议的,可以当场予以收缴罚款。罚款应当开具省、自治区、直辖市财政部门统一制发的罚款收据;不出具财政部门统一制发的罚款收据的,当事人有权拒绝缴纳罚款。

  • 美菱冰箱的温度怎么调才正确(美菱冰箱温度调节怎么做)

    美菱冰箱温度调节怎么做?想要更好的来储存食物的,首先我们就要把冰箱的温度调试好,美菱冰箱温控是我们现在的家里很常见的一种电器,给我们的生活带来了很多方便,那么下面小编就带领大家去了解一下。当室温低于10°C时,温控器应拨到“6”的位置。7为最高档位,调后为冰箱的速冻状态,使用后需要调回、否则冰箱将不停机的工作。

  • 垂直电商的现状(垂直电商的至暗时刻)

    截至12月28日,蘑菇街的市值仅剩4029万美元。“目前内部员工爆料是整体裁员差不多30%,研发重灾区裁员80%。运维部门仅剩3人,产品岗则仅剩2人。”但这样的消息,结合其不久前发布的财报数据,足够暴露出蘑菇街当下堪忧的境况。平台总GMV为54.54亿元,其中直播GMV达49.72亿元,同比增长8.5%。受限于较低的行业天花板,垂直电商之所以普遍遭遇发展难题,归根到底还是模式问题。

  • 如何辨别母鸡快下蛋的特征(母鸡快下蛋的特点)

    如何辨别母鸡快下蛋的特征?下面内容希望能帮助到你,我们来一起看看吧!如何辨别母鸡快下蛋的特征母鸡快要下蛋的特征:最明显的就是小母鸡变得温顺,用手轻抓会蹲伏不动,冠发育明显,耻骨开3指左右。母鸡初次下单前会咯咯咯的叫个不停,出现脸红等症状,还会找个隐蔽的地方或是到窝准备生蛋。因为母鸡的肛门直径很小,尽管周围的肌肉有很大弹性,但是鸡蛋下出的过程中仍然是需要憋足劲的。

  • 早安的意思代表什么(早安有什么含义)

    早安的意思代表什么早安是指早晨的问好,关于早安有很多不同的意思,在恋人之间的意思是指还好,我们都活着,是对最爱人的期许,希望她每个早晨都安然无恙地醒来。其实这世间所有的荣华富贵,名名利利,官官权权,都是过眼云烟,都抵不上每天早上醒来第一句早安,睡觉前一句晚安,最暖心。一定要珍惜那些每个清晨和你说“早安”的人,因为没有人应该那么做。

  • 天津普通超白玻璃参考价格(科创质量带你来认识下)

    水玻璃即硅酸钠,是由碱金属氧化物和二氧化硅结合而成的可溶性碱金属硅酸盐材料,又称泡花碱。水玻璃在建筑装饰方面的用途是非常大的,那么,如何能使它被牢固得粘接起来呢?无机胶黏剂磷酸盐胶黏剂应用最多的就是水玻璃,其特点是易溶于水,有较好的粘接能力和耐热性能。

  • 电视剧我怕来不及钱向利怎么死的 我怕来不及钱向利被杀是哪一集

    3、该剧讲述了改革风浪中,以李春生为代表的中国工人迎难而上、越挫越勇,肩负小家与大家的故事。

  • 牛油果里面为什么会有黑色纤维(牛油果有黑色纤维的原因)

    下面内容希望能帮助到你,我们来一起看看吧!牛油果里面为什么会有黑色纤维这是由于受伤水果的表皮以及内部充当“保护墙”的薄膜破裂,使氧气进入水果内部造成的。氧气会与水果中的一些化合物发生反应,把这些化合物氧化。因为柠檬酸非常容易被氧化,因此可以用它来清除氧气,防止水果变黑。这就是为什么如果把苹果片放在柠檬汁中浸一下后,苹果片能够在很长时间内不变黑的原因。