C#表达式目录树示例详解

1、表达式目录树

  表达式目录树,在C#中是Expression来定义的,它是一种语法树,或者说是一种数据结构。其主要用于存储需要计算、运算的一种结构,它只提供存储功能,不进行运算。通常Expression是配合Lambda一起使用,lambda可以是匿名方法。Expression可以动态创建。

  声明一个lambda表达式,其中可以指明类型,也可以是匿名方法:

//Func<int, int, int> func = new Func<int, int, int>((m, n) => m * n + 2);
Func<int, int, int> func = (m, n) => m * n + 2;

  上述代码可以使用Expression来定义:

Expression<Func<int, int, int>> exp = (m, n) => m * n + 2;//lambda表达式声明表达式目录树

  Expression的方法体只能是一个整体,不能具有花括号,以下代码是不允许的:

Expression<Func<int, int, int>> exp1 = (m, n) =>//方法体只能一体
{
 return m * n + 2;
};

  上述func和exp执行结果相同:

int iResult1 = func.Invoke(3, 2);
int iResult2 = exp.Compile().Invoke(3, 2);

2、构建表达式目录树

  上述表达式示例可以通过Expression来自主构建,把m、n定义为ParameterExpression参数,把2定义为常数表达式ConstantExpression,使用Expression的静态方法,表示乘和加:

ParameterExpression parameterLeft = Expression.Parameter(typeof(int), "m");//定义参数
ParameterExpression parameterRight = Expression.Parameter(typeof(int), "n");//定义参数
BinaryExpression binaryMultiply = Expression.Multiply(parameterLeft, parameterRight);//组建第一步的乘法
ConstantExpression constant = Expression.Constant(2, typeof(int)); //定义常数参数
BinaryExpression binaryAdd = Expression.Add(binaryMultiply, constant);//组建第二步的加法
var expression = Expression.Lambda<Func<int, int, int>>(binaryAdd, parameterLeft, parameterRight);//构建表达式
var func = expression.Compile(); //编译为lambda表达式
int iResult3 = func(3, 2);
int iResult4 = expression.Compile().Invoke(3, 2);
int iResult5 = expression.Compile()(3, 2);

  自主构建Expression是,参数名称的定义,可以不是m、n,可以是其他的a、b或者x、y。

  如何构建一个复杂的表达式目录树?需要使用到Expression中更多的方法、属性、扩展方法等。首先定义一个类:

public class People
{
 public int Age { get; set; }
 public string Name { get; set; }
 public int Id;
}

  基于上面的类,构建表达式: Expression<Func<People, bool>> lambda = x => x.Id.ToString().Equals("5");

  这个示例中,使用到了int自身的ToString()方法,还使用到了字符串的Equals方法。构建过程如下:

//以下表达式目录树实现lambda的表达式
Expression<Func<People, bool>> lambda = x => x.Id.ToString().Equals("5");
//声明一个参数对象
ParameterExpression parameterExpression = Expression.Parameter(typeof(People), "x");
//查找字段, 并绑定访问参数对象字段(属性)的方法:x.Id
MemberExpression member = Expression.Field(parameterExpression, typeof(People).GetField("Id"));
//以上可以用这个代替
var temp =Expression.PropertyOrField(parameterExpression, "Id");
//调用字段的ToString方法:x.Id.ToString()
MethodCallExpression method = Expression.Call(member, typeof(int).GetMethod("ToString", new Type[] { }), new Expression[0]);
//调用字符串的Equals方法:x.Id.ToString().Equals("5")
MethodCallExpression methodEquals = Expression.Call(method, typeof(string).GetMethod("Equals", new Type[] { typeof(string) }), new Expression[]
{
 Expression.Constant("5", typeof(string))//与常量进行比较,也可以是参数
});
//创建目录树表达式
ar expression = Expression.Lambda<Func<People, bool>>(methodEquals, new ParameterExpression[] {parameterExpression });
bool bResult = expression.Compile().Invoke(new People()
{
 Id = 5,
 Name = "Nigle",
 Age = 31
});

3、使用Expression来进行不同对象的相同名字的属性映射

  前面构建了类People,现在我们构建一个新的类PeopleCopy:

public class PeopleCopy
{
 public int Age { get; set; }
 public string Name { get; set; }
 public int Id;
}

  现在声明一个People对象,然后对People对象的数据进行拷贝到PeopleCopy新对象中去,直接硬编码的方式:

  1. 硬编码

People people = new People()
{
 Id = 11,
 Name = "Nigle",
 Age = 31
};
PeopleCopy peopleCopy = new PeopleCopy()
{
 Id = people.Id,
 Name = people.Name,
 Age = people.Age
};

  如果这样编写,对于属性或者字段比较多的类,在拷贝时,我们需要编写很多次赋值,代码也会很长。此时,我们能想到的是通过反射的方式进行拷贝:

  2. 反射拷贝

public static TOut Trans<TIn, TOut>(TIn tIn)
{
 TOut tOut = Activator.CreateInstance<TOut>();
 foreach (var itemOut in tOut.GetType().GetProperties())
 {
  foreach (var itemIn in tIn.GetType().GetProperties())
  {
   if (itemOut.Name.Equals(itemIn.Name))
   {
    itemOut.SetValue(tOut, itemIn.GetValue(tIn));
    break;
   }
  }
 }
 foreach (var itemOut in tOut.GetType().GetFields())
 {
  foreach (var itemIn in tIn.GetType().GetFields())
  {
   if (itemOut.Name.Equals(itemIn.Name))
   {
    itemOut.SetValue(tOut, itemIn.GetValue(tIn));
    break;
   }
  }
 }
 return tOut;
}

  通过反射,我们可以通过输出类型的属性或者字段去查找原类型对应的属性和字段,然后获取值,并设置值的方式进行赋值拷贝。除此之外,我们还能想到的是深克隆的序列化方式,进行反序列化数据:

  3. 序列化和反序列化

public class SerializeMapper
{
 /// <summary>序列化反序列化方式/summary>
 public static TOut Trans<TIn, TOut>(TIn tIn)
 {
  //采用的是json序列化,也可以采用其他序列化方式
  return JsonConvert.DeserializeObject<TOut>(JsonConvert.SerializeObject(tIn));
 }
}

  前面的三种方法是最为常用的方法,但未使用到本文介绍的表达式目录树。如何将表达式目录树与拷贝结合起来?有两种方式【缓存+表达式目录】,【泛型+表达式目录】

  4. 缓存+表达式目录

/// <summary>
/// 生成表达式目录树 缓存
/// </summary>
public class ExpressionMapper
{
 private static Dictionary<string, object> _Dic = new Dictionary<string, object>();

 /// <summary>
 /// 字典缓存表达式树
 /// </summary>
 public static TOut Trans<TIn, TOut>(TIn tIn)
 {
  string key = string.Format("funckey_{0}_{1}", typeof(TIn).FullName, typeof(TOut).FullName);
  if (!_Dic.ContainsKey(key))
  {
   ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p");
   List<MemberBinding> memberBindingList = new List<MemberBinding>();
   foreach (var item in typeof(TOut).GetProperties())
   {
    MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name));
    //绑定Out和In之间的关系:Age = p.Age
    MemberBinding memberBinding = Expression.Bind(item, property);
    memberBindingList.Add(memberBinding);
   }
   foreach (var item in typeof(TOut).GetFields())
   {
    MemberExpression property = Expression.Field(parameterExpression, typeof(TIn).GetField(item.Name));
    MemberBinding memberBinding = Expression.Bind(item, property);
    memberBindingList.Add(memberBinding);
   }
   MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());
   Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, parameterExpression);
   Func<TIn, TOut> func = lambda.Compile();//拼装是一次性的
   _Dic[key] = func;
  }
  return ((Func<TIn, TOut>)_Dic[key]).Invoke(tIn);
 }
}

  5. 泛型+表达式目录

/// <summary>
/// 生成表达式目录树 泛型缓存
/// </summary>
/// <typeparam name="TIn"></typeparam>
/// <typeparam name="TOut"></typeparam>
public class ExpressionGenericMapper<TIn, TOut>//Mapper`2
{
 private static Func<TIn, TOut> func = null;
 static ExpressionGenericMapper()
 {
  ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p");
  List<MemberBinding> memberBindingList = new List<MemberBinding>();
  foreach (var item in typeof(TOut).GetProperties())
  {
   MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name));
   MemberBinding memberBinding = Expression.Bind(item, property);
   memberBindingList.Add(memberBinding);
  }
  foreach (var item in typeof(TOut).GetFields())
  {
   MemberExpression property = Expression.Field(parameterExpression, typeof(TIn).GetField(item.Name));
   MemberBinding memberBinding = Expression.Bind(item, property);
   memberBindingList.Add(memberBinding);
  }
  MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());
  Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[]
  {
    parameterExpression
  });
  func = lambda.Compile();//拼装是一次性的
 }
 public static TOut Trans(TIn t)
 {
  return func(t);
 }
}

  除了上述5中方法,还可以使用框架自带的AutoMapper,首先我们要nuget添加引用AutoMapper即可直接使用,具体代码为:

  6. AutoMapper

public class AutoMapperTest
{
 public static TOut Trans<TIn, TOut>(TIn tIn)
 {
  return AutoMapper.Mapper.Instance.Map<TOut>(tIn);
 }
}

  测评:对上述6种方式进行测评,每一种拷贝方式运行100 0000次:

Stopwatch watch = new Stopwatch();
watch.Start();
for (int i = 0; i < 1000000; i++)
{
 //测试六种方法
 PeopleCopy peopleCopy = new PeopleCopy() {Id = people.Id, Name = people.Name,Age = people.Age}; //直接赋值的方式复制--22ms
 //PeopleCopy peopleCopy = ReflectionMapper.Trans<People, PeopleCopy>(people);      //反射赋值的方式复制---1573ms
 //PeopleCopy peopleCopy = SerializeMapper.Trans<People, PeopleCopy>(people);      //序列化方式---2716ms
 //PeopleCopy peopleCopy = ExpressionMapper.Trans<People, PeopleCopy>(people);      //表达式目录树 缓存 复制---517ms
 //PeopleCopy peopleCopy = ExpressionGenericMapper<People, PeopleCopy>.Trans(people);    //表达式目录树 泛型缓存--77ms
 //PeopleCopy peopleCopy = AutoMapperTest.Trans<People, PeopleCopy>(people);       //AutoMapper---260ms
}
watch.Stop();
Console.WriteLine($"耗时:{ watch.ElapsedMilliseconds} ms");

4、表达式目录树构建SQL删选

  传统的sql在构建条件语句时,需要通过诸多判断,进而构建成完整的查询语句。

People p = new People()
{
 Id = 11,
 Name = "Nigle",
 Age = 31
};
//拼装sql的方式
string sql = "SELECT * FROM USER WHERE Id=1";
if (string.IsNullOrWhiteSpace(p.Name))
{
 sql += $" and name like '%{p.Name}%'";
}
sql += $" and age >{p.Age}";

  事实上,我们偶尔我们会使用linq查询或者lambda表达式,用于条件筛选,如var lambda = x => x.Age > 5; 事实上,我们可以构建上述Expression:

People p = new People()
{
 Id = 11,
 Name = "Nigle",
 Age = 31
};
//拼装表达式目录树,交给下端用
ParameterExpression parameterExpression = Expression.Parameter(typeof(People), "x");//声明一个参数
Expression propertyExpression = Expression.Property(parameterExpression, typeof(People).GetProperty("Age"));//声明访问参数属性的对象
//Expression property = Expression.Field(parameterExpression, typeof(People).GetField("Id"));
ConstantExpression constantExpression = Expression.Constant(5, typeof(int));//声明一个常量
BinaryExpression binary = Expression.GreaterThan(propertyExpression, constantExpression);//添加比较方法
var lambda = Expression.Lambda<Func<People, bool>>(binary, new ParameterExpression[] { parameterExpression });//构建表达式主体
bool bResult = lambda.Compile().Invoke(p); //比较值

5、修改表达式目录树

  本示例将把已经构建完成的表达式目录树的加法进行修改为减法。修改、拼接、读取节点,需要使用到ExpressionVisitor类,ExpressionVisitor类能动态的解耦,读取相关的节点和方法。

  ExpressionVisitor类中的Visit(Expression node)是解读表达式的入口,然后能够神奇的区分参数和方法体,然后将表达式调度到此类中更专用的访问方法中,然后一层一层的解析下去,直到最终的叶节点!

  首先编写OperationsVisitor类,用于修改:

internal class OperationsVisitor : ExpressionVisitor
{
 public Expression Modify(Expression expression)
 {
  return this.Visit(expression);
 }
 protected override Expression VisitBinary(BinaryExpression b)
 {
  if (b.NodeType == ExpressionType.Add)
  {
   Expression left = this.Visit(b.Left);
   Expression right = this.Visit(b.Right);
   return Expression.Subtract(left, right);
  }

  return base.VisitBinary(b);
 }
 protected override Expression VisitConstant(ConstantExpression node)
 {
  return base.VisitConstant(node);
 }
}

  然后,编写lambda表达式,进行修改并计算结果:

//修改表达式目录树
Expression<Func<int, int, int>> exp = (m, n) => m * n + 2;
OperationsVisitor visitor = new OperationsVisitor();
Expression expNew = visitor.Modify(exp);

int? iResult = (expNew as Expression<Func<int, int, int>>)?.Compile().Invoke(2, 3);

  Visit这个这个方法能够识别出来 m*n+2 是个二叉树,会通过下面的图然后一步一步的进行解析,如果遇到m*n 这会直接调用VisitBinary(BinaryExpression b)这个方法,如果遇到m或者n会调用VisitParameter(ParameterExpression node)这个方法,如果遇到2常量则会调用VisitConstant(ConstantExpression node)。

  ORM与表达式树目录的关系:

  经常用到EF,其实都是继承Queryable,然后我们使用的EF通常都会使用 var items = anserDo.GetAll().Where(x => x.OrganizationId == input.oid || input.oid == 0) ,where其实传的就是表达式目录树。EF写的where等lambda表达式,就是通过ExpressionVisitor这个类来反解析的!后面将构建模拟EF的解析方法。

6、构建模拟EF的表达式目录树解析

  首先,构建解析表达式目录树的方法,不能再使用默认的。

/// <summary>
/// 表达式目录树中的访问者
/// </summary>
internal class ConditionBuilderVisitor : ExpressionVisitor
{
 /// <summary>
 /// 用于存放条件等数据
 /// </summary>
 private Stack<string> _StringStack = new Stack<string>();
 /// <summary>
 ///
 /// </summary>
 /// <returns></returns>
 internal string Condition()
 {
  string condition = string.Concat(this._StringStack.ToArray());
  this._StringStack.Clear();
  return condition;
 }
 /// <summary>
 /// 如果是二元表达式
 /// </summary>
 /// <param name="node"></param>
 /// <returns></returns>
 protected override Expression VisitBinary(BinaryExpression node)
 {
  if (node == null) throw new ArgumentNullException("BinaryExpression");

  this._StringStack.Push(")");
  base.Visit(node.Right);//解析右边
  this._StringStack.Push(" " + ToSqlOperator(node.NodeType) + " ");
  base.Visit(node.Left);//解析左边
  this._StringStack.Push("(");

  return node;
 }
 /// <summary>
 ///
 /// </summary>
 /// <param name="node"></param>
 /// <returns></returns>
 protected override Expression VisitMember(MemberExpression node)
 {
  if (node == null)
   throw new ArgumentNullException("MemberExpression");
  this._StringStack.Push(" [" + node.Member.Name + "] ");
  return node;
  return base.VisitMember(node);
 }
 /// <summary>
 /// 将节点类型转换为Sql的操作符
 /// </summary>
 /// <param name="type"></param>
 /// <returns></returns>
 string ToSqlOperator(ExpressionType type)
 {
  switch (type)
  {
   case (ExpressionType.AndAlso):
   case (ExpressionType.And):
    return "AND";
   case (ExpressionType.OrElse):
   case (ExpressionType.Or):
    return "OR";
   case (ExpressionType.Not):
    return "NOT";
   case (ExpressionType.NotEqual):
    return "<>";
   case ExpressionType.GreaterThan:
    return ">";
   case ExpressionType.GreaterThanOrEqual:
    return ">=";
   case ExpressionType.LessThan:
    return "<";
   case ExpressionType.LessThanOrEqual:
    return "<=";
   case (ExpressionType.Equal):
    return "=";
   default:
    throw new Exception("不支持该方法");
  }
 }
 /// <summary>
 /// 常量表达式
 /// </summary>
 /// <param name="node"></param>
 /// <returns></returns>
 protected override Expression VisitConstant(ConstantExpression node)
 {
  if (node == null)
   throw new ArgumentNullException("ConstantExpression");
  this._StringStack.Push(" '" + node.Value + "' ");
  return node;
 }
 /// <summary>
 /// 方法表达式
 /// </summary>
 /// <param name="m"></param>
 /// <returns></returns>
 protected override Expression VisitMethodCall(MethodCallExpression m)
 {
  if (m == null) throw new ArgumentNullException("MethodCallExpression");

  string format;
  switch (m.Method.Name)
  {
   case "StartsWith":
    format = "({0} LIKE {1}+'%')";
    break;

   case "Contains":
    format = "({0} LIKE '%'+{1}+'%')";
    break;

   case "EndsWith":
    format = "({0} LIKE '%'+{1})";
    break;

   default:
    throw new NotSupportedException(m.NodeType + " is not supported!");
  }
  this.Visit(m.Object);
  this.Visit(m.Arguments[0]);
  string right = this._StringStack.Pop();
  string left = this._StringStack.Pop();
  this._StringStack.Push(String.Format(format, left, right));
  return m;
 }
}

  然后,外部就可以通过编写表达式目录树的查询条件,再通过这个类的实例进行解析成对应的SQL语句:

{
 Expression<Func<People, bool>> lambda = x => x.Age > 5 && x.Id > 5
            && x.Name.StartsWith("1")
            && x.Name.EndsWith("1")
            && x.Name.Contains("2");
 //“ x => x.Age > 5 && x.Id > 5”等同于sql语句
 string sql = string.Format("Delete From [{0}] WHERE {1}", typeof(People).Name, " [Age]>5 AND [ID] >5");
 ConditionBuilderVisitor vistor = new ConditionBuilderVisitor();
 vistor.Visit(lambda);
 Console.WriteLine(vistor.Condition());
}
{
 Expression<Func<People, bool>> lambda = x => x.Age > 5 && x.Name == "A" || x.Id > 5;
 ConditionBuilderVisitor vistor = new ConditionBuilderVisitor();
 vistor.Visit(lambda);
 Console.WriteLine(vistor.Condition());
}
{
 Expression<Func<People, bool>> lambda = x => x.Age > 5 || (x.Name == "A" && x.Id > 5);
 ConditionBuilderVisitor vistor = new ConditionBuilderVisitor();
 vistor.Visit(lambda);
 Console.WriteLine(vistor.Condition());
}
{
 Expression<Func<People, bool>> lambda = x => (x.Age > 5 || x.Name == "A") && x.Id > 5;
 ConditionBuilderVisitor vistor = new ConditionBuilderVisitor();
 vistor.Visit(lambda);
 Console.WriteLine(vistor.Condition());
}

7、连接表达式目录树

  表达式目录树除了可以修改外,我们还可以通过对其进行表达式目录树的拼接,将两个及其以上的表达式目录树进行拼接在一起。先编写一个新的NewExpressionVisitor,继承自ExpressionVisitor,用于拼接时,调用的。它是一个内部类,放在访问拼接类的内部ExpressionExtend。然后再编写对应的扩展方法:Add、Or、Not

/// <summary>
/// 合并表达式 And Or Not扩展
/// </summary>
public static class ExpressionExtend
{
 /// <summary>合并表达式 expLeft and expRight</summary>
 public static Expression<Func<T, bool>> And<T>(this Expression<Func<T,bool>> expLeft,Expression<Func<T,bool>> expRight)
 {
  //用于将参数名进行替换,二者参数不一样
  ParameterExpression newParameter = Expression.Parameter(typeof(T), "c");
  NewExpressionVisitor visitor = new NewExpressionVisitor(newParameter);
  //需要先将参数替换为一致的,可能参数名不一样
  var left = visitor.Replace(expLeft.Body);//左侧的表达式
  var right = visitor.Replace(expRight.Body);//右侧的表达式
  var body = Expression.And(left, right);//合并表达式
  return Expression.Lambda<Func<T, bool>>(body, newParameter);
 }
 /// <summary>合并表达式 expr1 or expr2</summary>
 public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
 {

  ParameterExpression newParameter = Expression.Parameter(typeof(T), "c");
  NewExpressionVisitor visitor = new NewExpressionVisitor(newParameter);
  //需要先将参数替换为一致的,可能参数名不一样
  var left = visitor.Replace(expr1.Body);
  var right = visitor.Replace(expr2.Body);
  var body = Expression.Or(left, right);
  return Expression.Lambda<Func<T, bool>>(body, newParameter);
 }
 public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> expr)
 {
  var candidateExpr = expr.Parameters[0];
  var body = Expression.Not(expr.Body);
  return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
 }
 /// <summary>参数替换者 </summary>
 class NewExpressionVisitor : ExpressionVisitor
 {
  public ParameterExpression _NewParameter { get; private set; }
  public NewExpressionVisitor(ParameterExpression param)
  {
   this._NewParameter = param;//用于把参数替换了
  }
  /// <summary> 替换</summary>
  public Expression Replace(Expression exp)
  {
   return this.Visit(exp);
  }
  protected override Expression VisitParameter(ParameterExpression node)
  {
   //返回新的参数名
   return this._NewParameter;
  }
 }
}

  下面是测试代码:

Expression<Func<People, bool>> lambda1 = x => x.Age > 5;
Expression<Func<People, bool>> lambda2 = p => p.Id > 5;
Expression<Func<People, bool>> lambda3 = lambda1.And(lambda2);
Expression<Func<People, bool>> lambda4 = lambda1.Or(lambda2);
Expression<Func<People, bool>> lambda5 = lambda1.Not();

List<People> people = new List<People>()
{
 new People(){Id=4,Name="123",Age=4},
 new People(){Id=5,Name="234",Age=5},
 new People(){Id=6,Name="345",Age=6},
};

List<People> lst1 = people.Where(lambda3.Compile()).ToList();
List<People> lst2 = people.Where(lambda4.Compile()).ToList();
List<People> lst3 = people.Where(lambda5.Compile()).ToList();

总结

到此这篇关于C#表达式目录树的文章就介绍到这了,更多相关C#表达式目录树内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C#简单实现表达式目录树(Expression)

    1.什么是表达式目录树 :简单的说是一种语法树,或者说是一种数据结构(Expression) 2.用Lambda声明表达式目录树: Expression<Func<int, int, int>> exp = (n, m) => n * m + 2; //表达试目录树的方法体只能是一行,不能有大括号.比如: //Expression<Func<int, int, int>> exp1 = (m, n) => // { // return m * n

  • C# 表达式目录树的应用详解

    使用表达式目录树实现两个不同类型的属性赋值: public class People { public int Age { get; set; } public string Name { get; set; } public int Id; } public class PeopleCopy { public int Age { get; set; } public string Name { get; set; } public int Id; } public class Class1 {

  • C#表达式目录树示例详解

    1.表达式目录树 表达式目录树,在C#中是Expression来定义的,它是一种语法树,或者说是一种数据结构.其主要用于存储需要计算.运算的一种结构,它只提供存储功能,不进行运算.通常Expression是配合Lambda一起使用,lambda可以是匿名方法.Expression可以动态创建. 声明一个lambda表达式,其中可以指明类型,也可以是匿名方法: //Func<int, int, int> func = new Func<int, int, int>((m, n) =&

  • python四则运算表达式求值示例详解

    目录 四则运算表达式求值 思路说明 算法步骤 代码 四则运算表达式求值 思路说明 使用双栈来实现——存放数值的栈 nums 与存放运算符的栈 ops. 算法步骤 对原始表达式字符串 exp 进行预处理, 将其转为一个元素对应一个数值或运算符的列表 explist. 遍历 explist , 每个元素依次压入对应的栈中. 每次压入后, 判断当前两栈顶是否可进行乘除运算.栈顶可进行乘除运算的充要条件是, ops 栈顶为<*> ,</> 之一, 且 nums 中的元素比 ops 中的元素

  • vue实例成员 插值表达式 过滤器基础教程示例详解

    目录 一. 什么是Vue 二.为什么学Vue 三.如何使用Vue 下载安装? 插值表达式 四.vue特点 1.虚拟DOM 2.数据的双向绑定 3.单页面应用 4.数据驱动 五.Vue实例 六.实例成员 - 挂载点 | el - 自定义插值表达式括号| delimiters - 数据 | data - 过滤器 | filters - 方法 | methods - js对象(即字典)补充 - 插值表达式转义 | delimters - 计算属性 | computed - 监听属性 | watch 一

  • java数据结构图论霍夫曼树及其编码示例详解

    目录 霍夫曼树 一.基本介绍 二.霍夫曼树几个重要概念和举例说明  构成霍夫曼树的步骤 霍夫曼编码 一.基本介绍 二.原理剖析 注意: 霍夫曼编码压缩文件注意事项 霍夫曼树 一.基本介绍 二.霍夫曼树几个重要概念和举例说明  构成霍夫曼树的步骤 举例:以arr = {1  3  6  7  8   13   29}  public class HuffmanTree { public static void main(String[] args) { int[] arr = { 13, 7, 8

  • ThinkPHP Where 条件中常用表达式示例(详解)

    Where 条件表达式格式为: $map['字段名'] = array('表达式', '操作条件'); 其中 $map 是一个普通的数组变量,可以根据自己需求而命名.上述格式中的表达式实际是运算符的意义: ThinkPHP运算符 与 SQL运算符 对照表 TP运算符 SQL运算符 例子 实际查询条件 eq = $map['id'] = array('eq',100); 等效于:$map['id'] = 100; neq != $map['id'] = array('neq',100); id !

  • JavaScript console对象与控制台使用示例详解

    目录 1. console对象 2. console的静态方法 3. 自定义console 4. 控制台命令行API 4.1 $_ 4.2 $0-$4 4.3 $(selector) 4.4 $x(path) 4.5 inspect(obj) 4.6 keys()和values() 4.7 其它的命令 1. console对象 console对象是JavaScript的原生对象,提供了很多用于调试的方法,如console.log输出信息,console.count记录执行次数 console.l

  • babel插件去除console示例详解

    目录 起因 介绍 窥探 初见AST Program ExpressionStatement CallExpression MemberExpression Identifier StringLiteral 公共属性 如何写一个babel插件? 构造visitor方法 去除所有console 增加env api 增加exclude api 增加commentWords api 细节完善 对于后缀注释 对于前缀注释 发布到线上 安装 使用 起因 已经颓废了很久 因为实在不知道写啥了 突然我某个同事对

  • Go语言学习教程之goroutine和通道的示例详解

    目录 goroutine 通道 Range 和 Close Select 官方留的两道练习题 等价的二叉树 网络爬虫 源码地址 goroutine goroutine是由Go运行时管理的轻量级线程. go f(x, y, z)在一个新的goroutine中开始执行f(x, y,z). goroutines运行在相同的地址空间中,所以对共享的内存访问必须同步.sync包提供了基本的同步原语(synchronization primitives),比如互斥锁(mutual exclusion loc

  • java中Servlet监听器的工作原理及示例详解

    监听器就是一个实现特定接口的普通java程序,这个程序专门用于监听另一个java对象的方法调用或属性改变,当被监听对象发生上述事件后,监听器某个方法将立即被执行. 监听器原理 监听原理 1.存在事件源 2.提供监听器 3.为事件源注册监听器 4.操作事件源,产生事件对象,将事件对象传递给监听器,并且执行监听器相应监听方法 监听器典型案例:监听window窗口的事件监听器 例如:swing开发首先制造Frame**窗体**,窗体本身也是一个显示空间,对窗体提供监听器,监听窗体方法调用或者属性改变:

随机推荐