问题

如何对 Expression 进行计算?

回答
要计算一个“Expression”(表达式),咱们得先明白它到底是什么。简单来说,表达式就是一串告诉你“怎么做”的指示,它包含了数字、变量(可以理解为代表未知数的符号,比如 x, y)以及各种运算符号,比如加号(+)、减号()、乘号()、除号(/),甚至还有括号()来规定运算的优先级。

计算一个表达式,就像是按照说明书一步一步地操作。这个“操作”的过程,我们通常称之为“求值”。

核心原则:遵循运算顺序

最关键的一点,计算表达式不是乱来的,它有一套严格的规则来决定先算谁后算谁,这就是“运算顺序”或者“优先级”。如果你不按顺序来,结果很可能就全错了。

想想看,你不会先算 2 + 3 5,然后得出 25,对吧?正确的做法是先算乘法 3 5 = 15,然后再算加法 2 + 15 = 17。

这个运算顺序通常是这样的:

1. 括号里的内容优先计算: 任何在括号里的表达式,无论多复杂,都要先把它计算出来,把它变成一个单独的数值。如果里面还有嵌套的括号,就得从最里面的括号开始算。
比如,`(2 + 3) 5`,你得先算出括号里的 `2 + 3` 等于 `5`,然后才能计算 `5 5`,得到 `25`。
再比如,`10 / (2 (1 + 1))`,最里层是 `(1 + 1)`,算出 `2`。然后变成 `10 / (2 2)`。接着算括号里的 `2 2`,得到 `4`。最后是 `10 / 4`,等于 `2.5`。

2. 指数和根号(如果存在): 在数学里,像平方(a²)、立方(a³)或者平方根(√a)这些运算,优先级比乘除法还要高。如果你的表达式里有这些,得先算它们。

3. 乘法和除法(从左到右): 当你算到没有括号,只有加减乘除的时候,乘法和除法是同一级别的,并且它们会从左边开始,依次进行计算。
例如,`20 / 4 2`,你不能先算 `4 2`,然后用 `20 / 8`。而是应该先算 `20 / 4` 等于 `5`,然后再算 `5 2`,得到 `10`。

4. 加法和减法(从左到右): 最后,就是加法和减法了。它们也是同一级别的,同样是从左到右依次计算。
比如,`10 3 + 2`,先算 `10 3` 等于 `7`,然后算 `7 + 2`,得到 `9`。

处理变量:代入值

如果你的表达式里有变量,比如 `2x + 5`,那么要计算它,你就需要知道 `x` 具体代表的是什么数字。这个过程叫做“代入”。一旦你把 `x` 的值代进去,这个表达式就变成了一个纯粹的数字表达式,然后就可以按照上面说的运算顺序来计算了。

如果 `x = 3`,那么 `2x + 5` 就变成了 `2 3 + 5`。然后先算乘法 `2 3 = 6`,再算加法 `6 + 5 = 11`。

计算机如何做?

其实,计算机在计算表达式时,就是严格按照这套规则来的。它们会把表达式解析开,识别出数字、变量、运算符和括号,然后用一种叫做“解析树”或者“后缀表示法”(如逆波兰表示法)的中间形式来存储,再根据运算优先级,一步步地执行运算。

总结一下,计算一个表达式,你可以想象自己是一个严谨的计算师:

审视整体: 先看看表达式里有什么,有没有括号?有乘除吗?有加减吗?
找最优先的: 优先处理括号里的,并且要从最里面的括号算起。
步步为营: 按照“指数 > 乘除(左到右)> 加减(左到右)”的顺序,一次只进行一种运算。
代入未知: 如果有变量,别忘了先给它填上具体的数值。

把这些规则记在心里,你就能准确地计算出绝大多数数学表达式的值了。这就像解谜一样,每一步都得小心谨慎。

网友意见

user avatar
@Ivony

大大的回答正解了。

俺就来直接回答一下题主的那几个问题:

其中,当Body == Parameters[0] 时,使用新的ParameterExpression 。
我有三个问题:
1、两者相等意味着什么?为什么这样处理?

并不是Body == Parameters[0],而是Expression<TDelegate>.Body所指向的expression tree中任何节点与这个指定的Parameters[0]相同时,在visitor返回的新expression tree中将这个节点替换为新的ParameterExpression。

这个操作的意思是什么

@Ivony

大大的回答已经说得很清楚了。

expr1与expr2的类型都是Expression<Func<T, bool>>,换句话说这个lambda的函数类型是接受一个类型为T的参数、返回值类型为bool的形式。所以在它们里面肯定只会有1个ParameterExpression,也就是由它们的Parameters[0]所引用的那个。

要提取出expr1与expr2的函数体,将它们组装出一个新的lambda,就必须要把它们的参数替换为新lambda的参数,否则无法建立起新lambda的参数与expr1/expr2已有的函数体之间的联系。

其实要组装expr1与expr2,除了提取出它们的函数体然后直接拼装之外,还可以选择直接调用它们然后组合结果,例如说:

       using System; using System.Linq.Expressions;  static class Program {   static Expression<Func<T, bool>> AndAlso<T>(       this Expression<Func<T, bool>> expr1,       Expression<Func<T, bool>> expr2) {     var param = Expression.Parameter(typeof(T));     var invoke1 = Expression.Invoke(expr1, param);     var invoke2 = Expression.Invoke(expr2, param);     var combine = Expression.AndAlso(invoke1, invoke2);     return Expression.Lambda<Func<T, bool>>(combine, param);   }    static void Main(string[] args) {     Expression<Func<int, bool>> expr1 = a => a >= 0;     Expression<Func<int, bool>> expr2 = b => b < 10;     var combined = expr1.AndAlso(expr2);     var func = combined.Compile();     Console.WriteLine("{0}, {1}", func(2), func(42));     // prints:     // True, False   } }      

这个例子中,

expr1是

       a => a >= 0      

expr2 是

       b => b < 10      

而组合出来的combined的逻辑是:

       x => expr1(x) && expr2(x)      

也就是直接调用expr1与expr2得到结果来做&&操作得到结果(注意这里有短路求值语义喔,虽然这个例子中没有副作用看不出来短路与否的区别)。

而"Invoke",函数调用,用lambda演算的角度来看,就是β-reduction——function application。

expr1(x) ,换个写法就是 (a => a >= 0)(x) ,经过β-reduction之后得到的就是 x >= 0。

对 expr2(x) 如法炮制,(b => b < 10)(x) 应用β-reduction得到 x < 10。

把这俩放在combined中,就变成了:

       x => (x >= 0) && (x < 10)     

可以看到,正如

@Ivony

大大的回答所说,这就是“把函数内联”的效果。

题主所给出的示例代码中,把expr1与expr2的函数体提取出来,并将它们的参数替换为combined的参数,这就是β-reduction。

2、如果我改写成OrElse,这里是不是有影响?

如果您需要的组合方式是 expr1(x) || expr2(x) 的话,那么OrElse()就对了

3、如果想深入学习该方面的知识,有没有推荐的书籍?

这个问题好难回答…其实都是一些编译原理的基本原理的应用,混入一些lambda演算的皮毛。

LINQ Expression Tree / DLR Expression Tree就是一种比较典型的树形/DAG的IR,对它的各种操作都是编译器中端会涉及的知识,所以如果有一些编译原理基础的话学习起来就会很轻松。

不过没有基础也没关系,就是棵表达程序语义的树/DAG而已…

我以前写过一些博客文章跟LINQ与DLR Expression Tree相关,如果有兴趣也可以参考一下:

RednaxelaFX的博客 - DLR分类文章列表 - ITeye技术网站

有本书介绍DLR(与LINQ Expression Tree)的特性和用法的,或许可以参考:

Pro DLR in .NET 4
user avatar

简单说就是这样,我们假定有两个函数:

       a => a < 10 b => b > 0     

其中,a和b是

       expr1.Parameters[0] 和 expr2.Parameters[0]      
       a < 10 b > 0      

则是

       expr1.Body 和 expr2.Body      

所以,其实就是产生这样一个东西:

       x =>     //x 就是 parameter = Expression.Parameter(typeof (T));   x < 10 //第一个函数,用x替换了a   &&     //AndAlso   x > 0  //第二个函数,用x替换了b      

就是这个样子:

       x => x < 10 && x > 0      

所以简单说,就是把函数内联。


书籍我自己也想找人给我推荐,我啃DLR也很费劲,,,,

类似的话题

  • 回答
    要计算一个“Expression”(表达式),咱们得先明白它到底是什么。简单来说,表达式就是一串告诉你“怎么做”的指示,它包含了数字、变量(可以理解为代表未知数的符号,比如 x, y)以及各种运算符号,比如加号(+)、减号()、乘号()、除号(/),甚至还有括号()来规定运算的优先级。计算一个表达式.............
  • 回答
    对一段文本进行寻根溯源,尤其以“拿破仑进军巴黎”为例,这是一个非常有价值的技能,它能帮助我们理解文本的来源、作者的意图、文本的可靠性以及它所处的历史和文化背景。下面我将详细阐述如何对“拿破仑进军巴黎”这段文本进行寻根溯源,并提供一个假设的文本作为例子来演示整个过程。核心理念:追溯信息来源的路径,验证.............
  • 回答
    托马斯·阿奎那关于上帝存在的因果证明(Cosmological Arguments for the Existence of God)是西方哲学史上影响最深远的神学论证之一。阿奎那的论证主要集中在追溯事物存在的起点,寻找一个“第一因”(First Cause)或“必然存在者”(Necessary B.............
  • 回答
    您好!这是一个经典的数学谜题,用四个零进行运算得到 24。由于零的特性,直接用加减乘除会比较棘手。答案是:无法直接用标准的加、减、乘、除四种基本运算对四个零进行运算得到 24。让我们来详细解释一下为什么:理解数字零的特性 加法: 任何数字加上零都等于它本身 (a + 0 = a)。 减法: .............
  • 回答
    科研的生命力在于探索未知,而保持这份探索的热情,就像呵护一株植物,需要细心照料,不断注入新的养分。新鲜感不是凭空而来的,而是我们主动去创造和维护的。以下是我对如何保持科研新鲜感的一些思考和实践,希望能给你一些启发。一、 跳出“舒适区”,拥抱“未知区”很多人刚开始做科研时,激情满满,因为一切都是新鲜的.............
  • 回答
    知识分类这事儿,听起来好像挺学术,但咱们老百姓也天天都在做,只是没意识到。就好比你整理房间,把衣服放衣柜,书放书架,碗碟放橱柜,这就是一种最朴素的分类。那么,怎样才能把脑子里的海量信息,或者从网上、书本里吸收的零碎知识,系统地归拢起来呢?说白了,就是找个“地儿”放,并且能方便地再找到。咱就从几个维度.............
  • 回答
    对都市传说进行分类是一个有趣且富有挑战性的任务,因为都市传说本身具有高度的灵活性、多样性和文化渗透性。一个好的分类系统应该能够捕捉到它们的核心特征,并帮助我们理解其传播机制、社会功能以及对人们心理的影响。以下是一个详细的都市传说分类方法,结合了多种维度和考虑因素:核心分类原则:在开始具体分类之前,我.............
  • 回答
    幼儿数学启蒙,可不是让他们死记硬背加减乘除哦!那太枯燥了,也压根不是数学的精髓。真正的数学启蒙,是让孩子在玩耍中、在生活中,慢慢感受到数学的逻辑、规律和美妙。咱们的目标是培养孩子解决问题的能力、观察力、空间感和逻辑思维,让他们觉得数学是个好玩的东西,而不是畏惧的对象。核心理念:玩中学,用中悟别把“数.............
  • 回答
    关于如何对F22战斗机进行预警,这确实是一个复杂且充满挑战的课题。F22“猛禽”作为一款第五代战斗机,其设计初衷就是为了在高度复杂的电磁环境下实现超视距作战和制胜。要对其进行有效的预警,需要综合运用多种技术手段,并且需要深刻理解其核心优势和潜在的探测弱点。首先,我们必须认识到F22的强大之处。它的隐.............
  • 回答
    在 R 语言中对每一行数据求和是一个非常常见的操作,它能帮助我们快速地汇总每一条记录的信息。这就像我们拿到一份表格数据,想知道每一行(比如每一个学生的各科成绩)的总分一样。别担心,R 提供了非常方便的方法来完成这个任务,而且操作起来一点也不复杂。咱们就一步一步来,把这个过程讲得透透彻彻。 什么是“按.............
  • 回答
    哎呀,同学们,咱们今天来聊点儿特别有意思的,关于咱们家里、学校,甚至整个社会里,东西到底归谁管、谁说了算的事儿。这说白了,就是“私有制”和“公有制”这俩大概念。听起来有点儿严肃哈,但其实咱们身边处处都有它们的影子,我保证今天讲完,你们都能明明白白,以后再听大人说起,也能插上话了。咱们先从最简单的说起.............
  • 回答
    想象一下,汽车引擎就像一个需要喝水才能工作的“工人”。给这个工人“喝水”(燃油)的方式有几种,直喷和多点电喷就是其中两种。咱们就用最简单的方式来聊聊它们到底是怎么回事,以及哪个更好。 直喷 vs. 多点电喷:给引擎喝水的新老方式咱们先从“多点电喷”说起,你可以把它想象成是“老式”的给引擎送水方法。 .............
  • 回答
    嘿,各位家长朋友们!看到家里的宝贝一天天长大,是不是也想给他们接触点新东西,打开认识世界的新窗口?今天咱们就来聊聊,怎么让咱们两三岁的小娃娃,系统地开始接触咱们的邻国——日本的语言,也就是日语启蒙。别觉得这个年龄段太小,其实他们的小脑袋瓜里装着无限可能呢!咱们要明确一点,对两三岁的孩子进行语言启蒙,.............
  • 回答
    当我们谈论“归因”,其实是在说我们如何理解事物发生的“原因”。这不仅仅是科学研究的核心,更是我们日常生活里解决问题、学习经验、与人交往的基石。我没法给你一个万能公式,因为归因这回事儿,它不是一件可以简单套用就能解决的“技术活”,更多的是一种观察、思考和判断的过程。要真正理解如何归因,咱们得从几个关键.............
  • 回答
    想在知乎上找到某位用户曾经写过的特定答案,但又记不清具体内容,这确实是个技术活儿,但也不是不可能。你需要像个侦探一样,从你仅有的线索出发,层层剥茧。下面我将为你详细拆解这个过程,让你知道该怎么做,并且确保这些方法听起来都是一个普通知乎用户会想到的,自然而然。首先,我们得承认,知乎作为一个内容平台,并.............
  • 回答
    用户聚类分析,说白了就是把行为相似的用户找出来,给他们打上标签,方便我们后续对不同群体进行精细化运营。这可不是简单地把用户扔进几个篮子里,里面有很多讲究。第一步:明确你的目标是什么?你为什么要对用户做聚类?是为了: 个性化推荐? 比如电商平台,想把喜欢运动鞋的用户和喜欢登山装备的用户分开,然后推.............
  • 回答
    电网状态建模,是理解、分析和优化电力系统运行的基石。它不仅仅是列出几个数字,而是要捕捉一个庞大、复杂、动态变化的系统在某一时刻的“全貌”。想象一下,电网就像一个庞大的生命体,它的“状态”就是它此刻的心跳、血压、体温、血液循环等等,这些信息都至关重要。为什么需要对电网状态进行建模?简单来说,是为了“知.............
  • 回答
    好的,我们来聊聊关于高微(多元微积分)和MAS(多智能体系统)在博弈论(Game Theory)中的结合,我尽量用一种自然、深入的方式来展开,就像和一位对这两个领域都感兴趣的朋友交流一样。设想一下,我们不是在做一份生硬的学术报告,而是想把这两者是如何“携手”在游戏理论中发挥作用这事儿,给说清楚、说明.............
  • 回答
    假设你手头有一个数组,里面装的都是“0”和“1”这两个数字。你想把这个数组变得规整,让所有的“0”都聚集在一边,所有的“1”都聚集在另一边。这其实是个挺常见的任务,我们可以用几种不同的方法来完成,而且这些方法都很高效,不需要把数据都捞出来放进新的容器里折腾。方法一:双指针法这是最直观也最常用的方法。.............
  • 回答
    好,咱们来聊聊如何对出绝妙的下联。这不仅仅是找几个字眼,更是一种意境、一种哲思、一种巧妙的呼应。要做到自然,不露痕迹,关键在于理解上联的精髓,并用一种同样有味道的方式去回应它。咱们先得剖析一下上联。因为没有具体的上联,我这里就以一个常见的、有一定深度的上联为例,咱们来一步步拆解,然后推导出下联的思路.............

本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度google,bing,sogou

© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有