表达式树练习实践:入门基础 目录 表达式树练习实践:入门基础 什么是表达式树 创建表达式树 lambda 创建表达式树 通过 API 创建表达式树 Expression< TDelegate > 解析/ 什么是表达式树 来自微软官方文档的定义: 表达式树以树形数据结构表示代码。 它能干什么呢? 你可以对表达式树中的代码进行编辑和运算。 创建表达式树 创建表达式树有两种方式:通过 lambda 表达式、通过 API。 创建表达式树的意思是,在此之前已经编写好每个结点,最后使用代码将所有结点组合起来,生成表达式树。 ParameterExpression d = Expression.Parameter(typeof(int), "y"); Expression r2 = Expression.Multiply(c, d); //乘法运行 Expression result = Expression.Add(r1, r2);
题记: 由于反射需要大量的性能开销,所以推荐用表达式树或者emit,但是emit 如果不熟悉指令编程的话,使用成本很大,所以优先推荐表达式树,但是网上给出来的文档 都非常的复杂,只是带你使用,刚好我团队的小伙伴也不太理解 ,所以我来整理一篇简单入门版本的 ps:问: 反射有3种方式,一个是获取值,一个是赋值,一个是调用方法 (如构造器 静态方法 普通方法等),哪个才是性能元凶 先总结: 表达式树 就是代码的拼接, 所以有以下三个区域 数组 Expression.Block() Expression.MemberInit() Expression.ListInit() 其实理论上来说,表达式树没有固定的返回的值,因为 表达式树中不允许出现 return,只是调用Compile()的推测, 所以其实 返回值 只有一个Block,其他全部是 代码块,只不过我觉得这时候应该区分出来, 这个也是所有的表达式树在调用Lamda 的时候标准写法都是 最后 在强调下,表达式树 其实 就是 入参 出参 返回的代码拼接,然后执行成 我们想要的代码,虽然看起来复杂,但是实际就是一步步来的.
这节来讲一下C#中的表达式树(又称表达式目录树、Expression)。 什么是表达式树? 表达式树是一种C#中的数据结构,它以树的形式表示某些代码内部的结构。 例如,你可以将一个表达式树转换为可重用的Lambda表达式,或者用于创建动态查询。或者,你可以遍历表达式树来读取和解析表达式的结构。 2. lambda表达式:lambda表达式是创建委托或表达式树类型的一种便捷方式。通过使用lambda表达式,你可以编写局部函数,这些函数可以在表达式或语句的上下文中使用。 最后,概述一下表达式的特点 1. 表达式树是代码的数据结构表示:与直接编写的代码不同,表达式树是代码的数据结构表示。它让你可以在运行时检查和操作数据,就像操作其他数据结构一样。 2. 表达式树可以被编译并执行:表达式树不仅可以表示代码,还可以被编译并执行。这使得表达式树比反射有更好的性能,因为反射需要在运行时解析类型和方法信息,而表达式树在编译后就可以直接执行。 4.
:表达式树的叶节点是操作数,其他节点是操作符。 这就是一颗表达式树,在这棵树中,只有叶节点是操作数,其他节点都是操作符。 我们先来遍历一下这棵树。 前序遍历这棵树将会得到这样一个表达式:++a*bc*de;(这样的表达式,我们称之为前缀表达式,操作符位于操作数之前。) 这样可以得到我们人喜欢使用的中缀表达式和计算机喜欢的后缀表达式。 构造一颗表达式树的算法:该算法描述的是将一颗后缀表达式转换成表达式树的方法。 这时候,栈中只剩一个元素,该元素就是这颗表达式树的根节点。 创建表达式树的代码实现如下,表达式的操作数是小写字母a~z,操作符可以是+,-,*,/,^,%等双目运算符。
为什么要学习表达式树?表达式树是将我们原来可以直接由代码编写的逻辑以表达式的方式存储在树状的结构里,从而可以在运行时去解析这个树,然后执行,实现动态的编辑和执行代码。 本系列计划三篇,第一篇主要介绍表达式树的创建方式。第二篇主要介绍表达式树的遍历问题。第三篇,将利用表达式树打造一个自己的LinqProvider。 本文主要内容: 有返回值的表达式树示例 通过表达式树访问类翻译SQL查询Where语句 上一篇由浅入深表达式树(一)我们主要讨论了如何根据Lambda表达式以及通过代码的方式直接创建表达式树。 当然,自己动手胜过他人讲解百倍,我相信只要你手动的去敲一些例子,你会发现创建表达式树其实并不复杂。 表达式的遍历 说完了表达式树的创建,我们来看看如何访问表达式树。 很明显,我们构造了一个Lambda表达式树,但是注意,我们没有直接Visit这Lambda表达式树,它是Visit了它的Body。它的Body是什么?
为什么要学习表达式树?表达式树是将我们原来可以直接由代码编写的逻辑以表达式的方式存储在树状的结构里,从而可以在运行时去解析这个树,然后执行,实现动态的编辑和执行代码。 本系列计划三篇,第一篇主要介绍表达式树的创建方式。第二篇主要介绍表达式树的遍历问题。第三篇,将利用表达式树打造一个自己的LinqProvider。 本文主要内容: 由Lambda表达式创建简单的表达式树 手动创建复杂的表达式树 表达式树类型列表及示例 创建一个简单的Lambda表达式树 在 上一篇Lambda表达式中我们提到了可以直接根据Lambda 表达式来创建表达式树,这应该是最直接的创建表达式树的方式了。 => ((num1 + num2) > 1000),539,281) 今天我们演示了如何通过代码的方式去创建表达式树,然后总结了一下.NET为我们提供的表达式类型。
IQueryable/IQueryable 和表达式树 IQueryable有两个组件 Expression:当前查询的组件的与语言和数据源无关的表示形式,以表达式树的形式表示。 在动态查询的上下文中,提供程序通常会保持不变;查询的表达式树将因查询而异。 达式树是不可变的;如果需要不同的表达式树并因此需要不同的查询,则需要将现有表达式树转换为新的表达式树,从而转换为新的 IQueryable。 从表达式树中使用运行时状态 内部表达式树以及查询尚未修改;查询只返回不同的值,因为 length 的值已更改。 使用工厂方法构造表达式树和查询 构造 Expression (截取片段) 构造要传入到某个 LINQ 方法的表达式时,实际上是在构造 Expression 的实例,其中 TDelegate 是某个委托类型
笔者最近学了表达式树这一部分内容,为了加深理解,写文章巩固知识,如有错误,请评论指出~ ? ---- 表达式树的概念 表达式树的创建有 Lambda法 和 组装法。 学习表达式树需要 委托、Lambda、Func<> 基础。 表达式树 形状可以参考 二叉树。 ? 可以把表达式树理解成 数学表达式。 数学表达式的所有常量、符号为表达式树的底节点。 int>> func = (i, j, x, y) => (i * j) + (x * y); 2,输出系统转换的表达式 输入这一行代码后运行,看看控制台输出的表达式树 Console.WriteLine 3,生成终结点 Expression result = Expression.Add(r1, r2); //相加 4,生成表达式树、转换、输出表达式树、代入数据进行运算 才能被系统自动转为表达式树 2,运算操作符 一般数学上,有加减乘除、取余、求幂等操作,而在程序中,运算操作符可以有更多的选择,达 85 种。
2、非捕获型括号(?:…) 这个是什么意思呢?意思是只分组不捕获。 好处: 1>避免了不必要的捕获操作,提高了匹配效率。 2>根据情况选择合适的括号能够叫逻辑更清晰,读者能清楚理解需要捕获的内容,而不用挨个数括号算作者到底想捕谁。 坏处:写法看着不够美观,增加了阅读难度,这个难度因人而异。 正则表达式怎么写? 逗号应该加在“左边有数字,右边数字的个数正好是3的倍数的位置”,但是一般正则表达式都是从左向右工作的,这时就要用到“环视”了。 <=…) 子表达式能够匹配左侧文本 不支持 否定逆序环视 (?<!…) 子表达式不能匹配左侧文本 不支持 肯定顺序环视 (?=…) 子表达式能够匹配右侧文本 支持 否定顺序环视 (?! 2、在多数系统中,使用范围表示法而不是列出范围内的所有字符并不会影响执行速度(例如:[0-9]与[0123456789]是一样的。)相反,如果某些实现方法没有优化好的话,还会是范围表示法会快一些。
表达式树是一种树形数据结构,通过动态语言运行时 (DLR) 将一组动态语言服务添加到公共语言运行时 (CLR),为静态类型语言添加动态特征。 C#属于静态语言.简而言之,就是通过CLR引入DLR,DLR中包含了表达式树的功能,那么C#代码就具备了将静态代码转换成动态代码的功能.常用于一些运算逻辑的转换.将运算逻辑转换成数据结构缓存到内存中.比如通过表达式树缓存通过反射构建对象的过程 , "")); 通过将计算规则存入数据库.然后调用DataTable的Api实现计算.但是这种方式显然不够灵活,且如果复杂的计算流程,配置起来会比较麻烦且容易出错.下面来看看表达式树怎么做: 经过一系列促销活动后的最终价格为{0}", price); Console.ReadKey(); 通过这种方式虽然能完成需求,但是这种方式任然需要通过硬编码的方式,显然不可取,且此时的表达式树虽然存储了所有的运算规则 ,但是这个规则只能是简单的数学运算,如果包含了负责的运算,则需要方法体,那么是不被允许的,如下图: 所以这种方式,需要将所有的运算逻辑全部转换成表达式树的形式即每一个节点都转换成表达式树,才可以,代码如下
IPV4 地址正则 IP 地址范围是0.0.0.0 ~ 255.255.255.255,也就是数字范围是0 ~ 255,如下分步拆解书写思路 当数字是一位时,\d 当数字是二位时,\d{2} 当数字是三位时 ,分三种情况 100 ~ 199: 1\d{2} 200 ~ 249: 2[0-4]\d 250 ~ 255: 25[0-5] .需要转义:\. xxx.重复3次:(xxx.){3} 末尾还是xxx 综合上述分析,IPV4正则如下 (((\d{1,2})|(1\d{1,2})|(2[0-4]\d)|(25[0-5]))\.){3}((\d{1,2})|(1\d{1,2})|(2[0- xx-重复5次:(xx:){5} 末尾还是x 综合上述分析,windows mac正则如下 ([0-9a-fA-F]{2}-){5}[0-9a-fA-F]{2} 同理,linux mac正则如下 ([ 0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2} 未完待续
作业讲解seq() 函数 # 1.生成1到15之间所有偶数seq(from = 2,to = 15,by = 2)## [1] 2 4 6 8 10 12 14##参数可以省略seq(2,15,2 )## [1] 2 4 6 8 10 12 14paste0()函数# 2.生成向量,内容为:"student2" "student4" "student6" "student8" "student10 " "student12" "student14" paste0(rep("student",times = 7),seq(from = 2, to = 15,by = 2))## [1] "student2 "paste0(rep("student",times = 7),seq(from = 2, to = 6,by = 2))## [1] "student2" "student4" "student6" 引用自生信技能树
二叉树-查找指定节点 以上一篇为基础来看看树中的查找。 问题 (1)编写前序查找,中序查找和后序查找的方法。 (2)并分别使用三种查找方式,查找treenode=5的节点。 this.root.PreOrder(); } else { Console.WriteLine("二叉树为空 = new TreeNode(4,"王五"); TreeNode node5 = new TreeNode(5, "赵六"); //手动创建二叉树 (5); if (treeNode2 ! 后序查找效率最高只需要查找2次。
从我的角度来看重复造轮子的原因有以下三种:
1、研究造轮子的原理
2、轮子不满足现在的开发需要
3、装B
表达式树的作用
最常用到的无非就是ORM的删查改的条件,ORM就是在ado.Net的基础上封装了一层表达式 那么我们能将表达式树解析成字符串,那么也能反过来。例如运费系统,在后台设置定义好一套计算规则。例如:对应不同的发货渠道,什么重量取哪个区间的费用,多于哪个阶段的费用还要额外费用。 我们可以通过解析这套计算规则拼装好表达式树传入参数进行计算。。。
还有别的在评论补充下。。。
不扯多,现在我们只拿解析表达式树来学习。 创建表达式
首先创建4个属性的Users类
1 namespace CG.ExpressionProject
2 {
3 ///
为何还把后缀表达式转换为二叉树,然后再在树的结构基础上求解,且不是饶了一个弯子,其实不然。 另受树相关算法的加持,也可以把后缀表达式的求解过程变得很易理解且具有艺术性。 2. 表达式树 如何把中缀表达式转换为后缀表达式,此文不再负赘。仅讲解如何把后缀表达式转换为表达式树,以及对表达式树求解。 并且讨论以树进行求解的益处。 现假设有如下后缀表达式:8571-*+82/-其中缀表达式为8+5*(7-1)-8/2。二叉树的构造过程如下: 2.1 构建流程 定义节点类型。 继续扫描8、2,因是操作数,其相应的节点类型直接入栈。至此,栈中的情形下图所示。 继续扫描表达式后面的/、-运算符,作上述相同的处理。最终表达式树如下图所示。 把后缀表达式映射成二叉树,其一,可以通过结构清晰看到后缀表达式的底层逻辑,其二可以基于树的算法直观易懂得到结果。再因节点是可以是复杂数据类型,可以在遍历树的过程中封装复杂的结果。
简介 虚树,顾名思义就是不真实的树。 它往往出现在一类树形动态规划问题中。 换句话说,虚树实际就是为了解决一类树形动态规划问题而诞生的! 我们考虑暴力dp 设$f[x]$为处理完以$x$为根的树的最小代价 转移分为两种情况 1.断开自己与父亲的联系,代价为从根到该节点的最小值 2.不考虑该节点(前提是该节点不是询问点),把子树内的所有询问点都断开的代价 于是,虚树诞生了 虚树 思想 虚树的主要思想是:对于一棵树,仅仅保留有用的点,重新构建一棵树 这里有用的点指的是询问点和它们的lca 煮个栗子 比如这样的一棵树(没错就是样例) ? 对于样例中的三次询问, 3 2 10 6 4 5 7 8 3 3 9 4 6 那么它的虚树分别长这样 ? ? ? 那么虚树中的点数是$O(2*k)$的。 这样复杂度就只与k有关,$O(2*\sum k_i)$。
,自动从语言层面的表达式转为表达式树。 Expression<Func<double, double>> exp = a => Math.Sin(a); 表达式树最终是一个内存中树状结构的数据。可以文本化,序列化、转存、传输等等。 运行时分析表达式的逻辑 序列化或者传输表达式 重新编译成可执行的代码 课后习题: //表达式求值时,验证表达式是否正确 LambdaExpression lambda = Expression.Lambda expb2 = Expression.Multiply(exp2, expB); BinaryExpression expab2 = Expression.Add(expA, expb2); //3 ,然后反编译出表达式来。
1.使用表达式目录树实现两个不同类型的属性赋值:
首先,准备两个实体类
///
整理了一下表达式树的一些东西,入门足够了 先从ConstantExpression 开始一步一步的来吧 它表示具有常量值的表达式 我们选建一个控制台应用程序 ConstantExpression Expression.Call(Expression.Constant(_pg), typeof(Program).GetMethod("ConsStr2"), _paraObj2); Expression<Func<object, string>> _meyLambdaState2 = Expression.Lambda<Func<object, string>>(_MyStateMethod2 ("返回值: " + s_tr2); ? 再下来我们讲什么呢,也许你猜到了UnaryExpression一元运算符表达式和 BinaryExpression 二元运算符表达式 我们先看一个这两个表达式的简单例子后,我们再做一个复杂的例子 UnaryExpression
JDK 也提供了大量的内置函数式接口供我们使用,使得 Lambda 表达式的运用更加方便、高效。 对接口的要求 虽然使用 Lambda 表达式可以对某些接口进行简单的实现,但并不是所有的接口都可以使用 Lambda 表达式来实现。 这个注解往往会和 lambda 表达式一起出现。 Lambda 基础语法 我们这里给出六个接口,后文的全部操作都利用这六个接口来进行阐述。 lambda 表达式引用方法 有时候我们不是必须要自己重写某个匿名内部类的方法,我们可以可以利用 lambda表达式的接口快速指向一个已经被实现的方法。 ((o1, o2) -> o1.getId() - o2.getId()); System.out.println(list); Lambda 表达式中的闭包问题 这个问题我们在匿名内部类中也会存在