说起Lambda表达式,大家基本都很熟悉了,而表达式树(Expression Trees),则属于80%的工作中往往都用不到的那种技术,所以即便不是什么新技术,很多人对其理解都并不透彻。此文意图从表达式树基本技术点结合实际应用,逐步来看表达式树究竟是怎么一回事,希望能帮助读者彻底学会表达式树^_^

一、初见表达式树Expression<TDelegate>

不妨回想一下,你第一次使用表达式树是在哪里呢,比如Expression<TDelegate>?

比如在linq查询中,常常用到Where方法过滤,OrderBy排序等,调用的是IQueryable<TSource>的静态扩展类Queryable的静态扩展方法。

 // C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\System.Core.dll
namespace System.Linq
{
//
// 摘要:提供一组用于查询实现 System.Linq.IQueryable`1 的数据结构的 static方法。
//
public static class Queryable
{
//...
public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
public static IOrderedQueryable<TSource> OrderBy<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector);
//...
}
}

可以看到里面的参数类型Expression<Func<TSource, bool>> predicate和Expression<Func<TSource, TKey>> keySelector,咦?这不就是表达式树(Expression<TDelegate>)吗?!是的,但不止如此。

Expression<TDelegate>是表达式树的一种,并不等同于表达式树。

二、从Expression<TDelegate>到LambdaExpression

看下面的代码:

1  Func<Person, bool> func1 = x => x.Age > ;
2  Func<Person, string> func2 = x => x.Name;
3  Expression<Func<Person, bool>> expression1 = x => x.Age > ;
4  Expression<Func<Person, string>> expression2 = x => x.Name;
5  //错误CS0834,无法将具有语句体的lambda表达式转换为表达式树
6  Func<Person, int, int> func3 = (x, y) => { return x.Age + y; };
7  Action<Person, int> func4 = (x, y) => { };
8  Expression<Func<Person, int, int>> expression3 = (x, y) => { return x.Age + y; };
9  Expression<Action<Person, int>> expression4 = (x, y) => { };

expression1和expression2可以编译通过,expression3和expression4则编译错误,提示"无法将具有语句体的Lambda表达式转换为表达式树 "。

这是因为,我们可以将Lambda表达式赋值给泛型表达式类型变量(Expression<TDelegate>),编译器会将我们的Lambda表达式编译为表达式树,但是仅限于expression lambdas(表达式Lambdas,也叫做single-line lambdas,即不具有语句体的Lambda表达式),相对的statement lambdas(语句Lambdas,也叫做multi-line lambdas),编译器则会报错。

看看反编译后的func1,func2,expression1,expression2。

    ParameterExpression expression3;
Func<Person, bool> func = x => x.Age > 0x12;
Func<Person, string> func2 = x => x.Name;
ParameterExpression[] parameters = new ParameterExpression[] { expression3 };
Expression<Func<Person, bool>> expression = Expression.Lambda<Func<Person, bool>>(Expression.GreaterThan(Expression.Property(expression3 = Expression.Parameter(typeof(Person), "x"), (MethodInfo) methodof(Person.get_Age)), Expression.Constant(0x12, typeof(int))), parameters);
ParameterExpression[] expressionArray2 = new ParameterExpression[] { expression3 };
Expression<Func<Person, string>> expression2 = Expression.Lambda<Func<Person, string>>(Expression.Property(expression3 = Expression.Parameter(typeof(Person), "x"), (MethodInfo) methodof(Person.get_Name)), expressionArray2);

expression1和expression2变成了一堆复杂的语句,这才是它们作为表达式树原有的面目^_^

而Expression<TDelegate>来自何方?它继承自LambdaExpression。

来看看Expression<TDelegate>反编译后的构造函数

 [__DynamicallyInvokable]
public sealed class Expression<TDelegate> : LambdaExpression
{
// 构造函数
internal Expression(Expression body, string name, bool tailCall, ReadOnlyCollection<ParameterExpression> parameters) :
6   base(typeof(TDelegate), name, body, tailCall, parameters)
{
}
//...
}

构造函数什么也没有做,只是将参数传递给父类构造函数,所以可以认为Expression<TDelegate>是对LambdaExpression的一层封装,而LambdaExpression又是继承自Expression,是表达式树的一种,而且是最特别的一种,为何特别,且看下文^_^

三、何为表达式树

首先来看下面的类图,可以更直观看到Expression<TDelegate>、LambdaExpression、Expression三者的关系。

黄线表示,使用表达式Lambdas赋值给Expression<TDelegate>类型变量,TDelegate会赋值给父类LambdaExpression的Type属性(联系下前面讲的构造函数的事情 ^_^ )

重点看绿线,LambdaExpression继承自Expression,而LambdaExpression的Body属性,又是Expression类型。

哇哦,眼尖的你注意到LambdaExpression还有个Compile()方法,它会编译Body,生成表示 lambda 表达式的可执行委托,然后你就可以调用委托了,所以真正的表达式树所谓的树,就是藏在Body里啦

So,其实Expression的子类型多达几十种,例如ParameterExpression,BinaryExpression,MethodCallExpression等等,但是只有LambdaExpression(Lambda表达式树)可以执行!

如果你还有疑问,那么任意一棵其它类型的表达式树,怎样执行呢?答案自然是创建一个新的LambdaExpression了,将表达式树作为LambdaExpression的Body,然后你懂的^_^

具体可以调用这个方法 public static Expression<TDelegate> Lambda<TDelegate>(Expression body, params ParameterExpression[] parameters);

此时,我们再来解答最初的疑问,究竟何为表达式树(Expression Trees)?

表达式树即一份树形结构存储的代码,树的每个结点又都是一个表达式树,你可以编译然后运行这份树形代码。

那么可以用它做什么呢?

  1. 表示Lambda表达式,这个显而易见了;
  2. 修改可执行代码,创建动态查询;
  3. 定制自己的IQueryable,通过翻译表达式树数据结构为特定的查询语言,实现对特定数据源的查询,即定制orm。与我们对各种数据库的Linq查询同理;

也就是说小到根据字段字符串参数的过滤排序等,大到定制自己的orm,你都离不开表达式树。

下篇文章中,我们将就表达式树的具体用途,来做实例展示。

四、手动创建表达式树

可以通过哪些途径来创建表达式树呢?通过上文我们知道可以使用表达式Lambda由编译器来创建Lambda表达式树,例如Expression<Func<int, bool>> lambda = num => num < 5;

而另外一个更通用的方式,就是引入System.Linq.Expressions命名空间,手动构造每一个表达式树结点,来创建表达式树。

来看几个简单的例子,一看就懂。

 class Program
{
static void Main(string[] args)
{
var ints = new int[] { , , , , , , , , , };
Func1(ints);
Func2(ints);
Func3(ints);
Console.Read();
}
/// <summary>
/// 目标表达式树 x => x > 5 && x <= 7
/// </summary>
public static void Func1(int[] ints)
{
//创建参数x
var parameter = Expression.Parameter(typeof(int), "x");
//创建表达式x>5
var con1 = Expression.Constant();
var bin1 = Expression.GreaterThan(parameter, con1);
//创建表达式x<=7
var con2 = Expression.Constant();
var bin2 = Expression.LessThanOrEqual(parameter, con2);
//组合两个表达式
var body = Expression.And(bin1, bin2);
//获取Lambda表达式
var lambda = Expression.Lambda<Func<int, bool>>(body, parameter);
var ints2 = ints.Where(lambda.Compile());
//执行结果
Console.WriteLine("\r\n Func1构造的表达式树Body:\r\n {0}", body);
Console.WriteLine("\r\n Func1构造的表达式树:\r\n {0}", lambda);
Console.WriteLine("\r\n Func1的结果:\r\n {0}", string.Join(",", ints2));
}
/// <summary>
/// 目标表达式树 x => x % 2 == 0 ? x : 0
/// </summary>
public static void Func2(int[] ints)
{
//创建参数 x
var parameter = Expression.Parameter(typeof(int), "x");
//创建表达式 x % 2
var con1 = Expression.Constant();
var bin1 = Expression.Modulo(parameter, con1);
//创建表达式 (x % 2) == 0
var con2 = Expression.Constant();
var bin2 = Expression.Equal(bin1, con2);
//创建表达式 x % 2 == 0 ? x : 0
var body = Expression.Condition(bin2, parameter, Expression.Constant());
//获取Lambda表达式
var lambda = Expression.Lambda<Func<int, int>>(body, parameter);
var ints2 = ints.Select(lambda.Compile());
//执行结果
Console.WriteLine("\r\n Func2构造的表达式树Body:\r\n {0}", body);
Console.WriteLine("\r\n Func2构造的表达式树:\r\n {0}", lambda);
Console.WriteLine("\r\n Func2的结果:\r\n {0}", string.Join(",", ints2));
}
/// <summary>
/// 目标表达式树 x => Console.WriteLine(x)
/// </summary>
/// <param name="ints"></param>
public static void Func3(int[] ints)
{
//创建参数i
var parameter = Expression.Parameter(typeof(int), "x");
//获取Console.WriteLine MethodInfo
MethodInfo method = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) });
//创建表达式
var body = Expression.Call(method, parameter);
var lambda = Expression.Lambda<Action<int>>(body, parameter);
Console.WriteLine("\r\n Func3构造的表达式树Body:\r\n {0}", body);
Console.WriteLine("\r\n Func3构造的表达式树:\r\n {0}", lambda);
Array.ForEach(ints, lambda.Compile());
}
}

输出结果:

怎么样,动手来写几个表达式树吧?!代码实战会让你更加轻松的入门表达式树技术。

这里贴出Func1中的lambda变量监视图,看看这棵树吧^_^

通过上面几个小例子,相信你对表达式树的手动创建已经基本了解,同时可以看到手动创建远没有使用表达式Lambda来的方便,但是却可以突破single-line lambdas限制实现更多强大的功能。

那么Expression有多少种呢?看下图。这些都是继承自Expression,至于每种的用途,可以在VS中自行浏览,使用时根据我们的实际用途来选用不同的Expression来构建表达式树即可。

这些表达式目录树节点的类型共有85种。

     //
// 摘要:
// 描述表达式目录树的节点的节点类型。
public enum ExpressionType
{
//
// 摘要:
// 加法运算,如 a + b,针对数值操作数,不进行溢出检查。
Add = ,
//
// 摘要:
// 加法运算,如 (a + b),针对数值操作数,进行溢出检查。
AddChecked = ,
//
// 摘要:
// 按位或逻辑 AND 运算,如 C# 中的 (a & b) 和 Visual Basic 中的 (a And b)。
And = ,
//
// 摘要:
// 条件 AND 运算,它仅在第一个操作数的计算结果为 true 时才计算第二个操作数。它与 C# 中的 (a && b) 和 Visual Basic 中的
// (a AndAlso b) 对应。
AndAlso = ,
//
// 摘要:
// 获取一维数组长度的运算,如 array.Length。
ArrayLength = ,
//
// 摘要:
// 一维数组中的索引运算,如 C# 中的 array[index] 或 Visual Basic 中的 array(index)。
ArrayIndex = ,
//
// 摘要:
// 方法调用,如在 obj.sampleMethod() 表达式中。
Call = ,
//
// 摘要:
// 表示 null 合并运算的节点,如 C# 中的 (a ?? b) 或 Visual Basic 中的 If(a, b)。
Coalesce = ,
//
// 摘要:
// 条件运算,如 C# 中的 a > b ? a : b 或 Visual Basic 中的 If(a > b, a, b)。
Conditional = ,
//
// 摘要:
// 一个常量值。
Constant = ,
//
// 摘要:
// 强制转换或转换运算,如 C#中的 (SampleType)obj 或 Visual Basic 中的 CType(obj, SampleType)。对于数值转换,如果转换后的值对于目标类型来说太大,这不会引发异常。
Convert = ,
//
// 摘要:
// 强制转换或转换运算,如 C#中的 (SampleType)obj 或 Visual Basic 中的 CType(obj, SampleType)。对于数值转换,如果转换后的值与目标类型大小不符,则引发异常。
ConvertChecked = ,
//
// 摘要:
// 除法运算,如 (a / b),针对数值操作数。
Divide = ,
//
// 摘要:
// 表示相等比较的节点,如 C# 中的 (a == b) 或 Visual Basic 中的 (a = b)。
Equal = ,
//
// 摘要:
// 按位或逻辑 XOR 运算,如 C# 中的 (a ^ b) 或 Visual Basic 中的 (a Xor b)。
ExclusiveOr = ,
//
// 摘要:
// “大于”比较,如 (a > b)。
GreaterThan = ,
//
// 摘要:
// “大于或等于”比较,如 (a >= b)。
GreaterThanOrEqual = ,
//
// 摘要:
// 调用委托或 lambda 表达式的运算,如 sampleDelegate.Invoke()。
Invoke = ,
//
// 摘要:
// lambda 表达式,如 C# 中的 a => a + a 或 Visual Basic 中的 Function(a) a + a。
Lambda = ,
//
// 摘要:
// 按位左移运算,如 (a << b)。
LeftShift = ,
//
// 摘要:
// “小于”比较,如 (a < b)。
LessThan = ,
//
// 摘要:
// “小于或等于”比较,如 (a <= b)。
LessThanOrEqual = ,
//
// 摘要:
// 创建新的 System.Collections.IEnumerable 对象并从元素列表中初始化该对象的运算,如 C# 中的 new List<SampleType>(){
// a, b, c } 或 Visual Basic 中的 Dim sampleList = { a, b, c }。
ListInit = ,
//
// 摘要:
// 从字段或属性进行读取的运算,如 obj.SampleProperty。
MemberAccess = ,
//
// 摘要:
// 创建新的对象并初始化其一个或多个成员的运算,如 C# 中的 new Point { X = 1, Y = 2 } 或 Visual Basic 中的 New
// Point With {.X = 1, .Y = 2}。
MemberInit = ,
//
// 摘要:
// 算术余数运算,如 C# 中的 (a % b) 或 Visual Basic 中的 (a Mod b)。
Modulo = ,
//
// 摘要:
// 乘法运算,如 (a * b),针对数值操作数,不进行溢出检查。
Multiply = ,
//
// 摘要:
// 乘法运算,如 (a * b),针对数值操作数,进行溢出检查。
MultiplyChecked = ,
//
// 摘要:
// 算术求反运算,如 (-a)。不应就地修改 a 对象。
Negate = ,
//
// 摘要:
// 一元加法运算,如 (+a)。预定义的一元加法运算的结果是操作数的值,但用户定义的实现可以产生特殊结果。
UnaryPlus = ,
//
// 摘要:
// 算术求反运算,如 (-a),进行溢出检查。不应就地修改 a 对象。
NegateChecked = ,
//
// 摘要:
// 调用构造函数创建新对象的运算,如 new SampleType()。
New = ,
//
// 摘要:
// 创建新的一维数组并从元素列表中初始化该数组的运算,如 C# 中的 new SampleType[]{a, b, c} 或 Visual Basic 中的
// New SampleType(){a, b, c}。
NewArrayInit = ,
//
// 摘要:
// 创建新数组(其中每个维度的界限均已指定)的运算,如 C# 中的 new SampleType[dim1, dim2] 或 Visual Basic 中的
// New SampleType(dim1, dim2)。
NewArrayBounds = ,
//
// 摘要:
// 按位求补运算或逻辑求反运算。在 C# 中,它与整型的 (~a) 和布尔值的 (!a) 等效。在 Visual Basic 中,它与 (Not a) 等效。不应就地修改
// a 对象。
Not = ,
//
// 摘要:
// 不相等比较,如 C# 中的 (a != b) 或 Visual Basic 中的 (a <> b)。
NotEqual = ,
//
// 摘要:
// 按位或逻辑 OR 运算,如 C# 中的 (a | b) 或 Visual Basic 中的 (a Or b)。
Or = ,
//
// 摘要:
// 短路条件 OR 运算,如 C# 中的 (a || b) 或 Visual Basic 中的 (a OrElse b)。
OrElse = ,
//
// 摘要:
// 对在表达式上下文中定义的参数或变量的引用。有关详细信息,请参阅System.Linq.Expressions.ParameterExpression。
Parameter = ,
//
// 摘要:
// 对某个数字进行幂运算的数学运算,如 Visual Basic 中的 (a ^ b)。
Power = ,
//
// 摘要:
// 具有类型为 System.Linq.Expressions.Expression 的常量值的表达式。System.Linq.Expressions.ExpressionType.Quote
// 节点可包含对参数的引用,这些参数在该节点表示的表达式的上下文中定义。
Quote = ,
//
// 摘要:
// 按位右移运算,如 (a >> b)。
RightShift = ,
//
// 摘要:
// 减法运算,如 (a - b),针对数值操作数,不进行溢出检查。
Subtract = ,
//
// 摘要:
// 算术减法运算,如 (a - b),针对数值操作数,进行溢出检查。
SubtractChecked = ,
//
// 摘要:
// 显式引用或装箱转换,其中如果转换失败则提供 null,如 C# 中的 (obj as SampleType) 或 Visual Basic 中的 TryCast(obj,
// SampleType)。
TypeAs = ,
//
// 摘要:
// 类型测试,如 C# 中的 obj is SampleType 或 Visual Basic 中的 TypeOf obj is SampleType。
TypeIs = ,
//
// 摘要:
// 赋值运算,如 (a = b)。
Assign = ,
//
// 摘要:
// 表达式块。
Block = ,
//
// 摘要:
// 调试信息。
DebugInfo = ,
//
// 摘要:
// 一元递减运算,如 C# 和 Visual Basic 中的 (a - 1)。不应就地修改 a 对象。
Decrement = ,
//
// 摘要:
// 动态操作。
Dynamic = ,
//
// 摘要:
// 默认值。
Default = ,
//
// 摘要:
// 扩展表达式。
Extension = ,
//
// 摘要:
// “跳转”表达式,如 C# 中的 goto Label 或 Visual Basic 中的 GoTo Label。
Goto = ,
//
// 摘要:
// 一元递增运算,如 C# 和 Visual Basic 中的 (a + 1)。不应就地修改 a 对象。
Increment = ,
//
// 摘要:
// 索引运算或访问使用参数的属性的运算。
Index = ,
//
// 摘要:
// 标签。
Label = ,
//
// 摘要:
// 运行时变量的列表。有关详细信息,请参阅System.Linq.Expressions.RuntimeVariablesExpression。
RuntimeVariables = ,
//
// 摘要:
// 循环,如 for 或 while。
Loop = ,
//
// 摘要:
// 多分支选择运算,如 C# 中的 switch 或 Visual Basic 中的 Select Case。
Switch = ,
//
// 摘要:
// 引发异常的运算,如 throw new Exception()。
Throw = ,
//
// 摘要:
// try-catch 表达式。
Try = ,
//
// 摘要:
// 取消装箱值类型运算,如 MSIL 中的 unbox 和 unbox.any 指令。
Unbox = ,
//
// 摘要:
// 加法复合赋值运算,如 (a += b),针对数值操作数,不进行溢出检查。
AddAssign = ,
//
// 摘要:
// 按位或逻辑 AND 复合赋值运算,如 C# 中的 (a &= b)。
AndAssign = ,
//
// 摘要:
// 除法复合赋值运算,如 (a /= b),针对数值操作数。
DivideAssign = ,
//
// 摘要:
// 按位或逻辑 XOR 复合赋值运算,如 C# 中的 (a ^= b)。
ExclusiveOrAssign = ,
//
// 摘要:
// 按位左移复合赋值运算,如 (a <<= b)。
LeftShiftAssign = ,
//
// 摘要:
// 算术余数复合赋值运算,如 C# 中的 (a %= b)。
ModuloAssign = ,
//
// 摘要:
// 乘法复合赋值运算,如 (a *= b),针对数值操作数,不进行溢出检查。
MultiplyAssign = ,
//
// 摘要:
// 按位或逻辑 OR 复合赋值运算,如 C# 中的 (a |= b)。
OrAssign = ,
//
// 摘要:
// 对某个数字进行幂运算的复合赋值运算,如 Visual Basic 中的 (a ^= b)。
PowerAssign = ,
//
// 摘要:
// 按位右移复合赋值运算,如 (a >>= b)。
RightShiftAssign = ,
//
// 摘要:
// 减法复合赋值运算,如 (a -= b),针对数值操作数,不进行溢出检查。
SubtractAssign = ,
//
// 摘要:
// 加法复合赋值运算,如 (a += b),针对数值操作数,进行溢出检查。
AddAssignChecked = ,
//
// 摘要:
// 乘法复合赋值运算,如 (a *= b),针对数值操作数,进行溢出检查。
MultiplyAssignChecked = ,
//
// 摘要:
// 减法复合赋值运算,如 (a -= b),针对数值操作数,进行溢出检查。
SubtractAssignChecked = ,
//
// 摘要:
// 一元前缀递增,如 (++a)。应就地修改 a 对象。
PreIncrementAssign = ,
//
// 摘要:
// 一元前缀递减,如 (--a)。应就地修改 a 对象。
PreDecrementAssign = ,
//
// 摘要:
// 一元后缀递增,如 (a++)。应就地修改 a 对象。
PostIncrementAssign = ,
//
// 摘要:
// 一元后缀递减,如 (a--)。应就地修改 a 对象。
PostDecrementAssign = ,
//
// 摘要:
// 确切类型测试。
TypeEqual = ,
//
// 摘要:
// 二进制反码运算,如 C# 中的 (~a)。
OnesComplement = ,
//
// 摘要:
// true 条件值。
IsTrue = ,
//
// 摘要:
// false 条件值。
IsFalse =
}

--参考阅读--

https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/expression-trees/

不可不知的表达式树(1)Expression初探的更多相关文章

  1. 不可不知的表达式树(3)定制IQueryProvider

    前面我们说到利用表达式树技术实现LINQ-to-SQL,实际上可以针对任何数据源,实现LINQ-to-Everything.这里还涉及到两个重要的接口即IQueryable和IQueryProvide ...

  2. 深入学习C#匿名函数、委托、Lambda表达式、表达式树类型——Expression tree types

    匿名函数 匿名函数(Anonymous Function)是表示“内联”方法定义的表达式.匿名函数本身及其内部没有值或者类型,但是可以转换为兼容的委托或者表达式树类型(了解详情).匿名函数转换的计算取 ...

  3. 表达式树(Expression Trees)

    [翻译]表达式树(Expression Trees) 原文地址:https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/con ...

  4. C# 表达式树(Expression)

    c#中有Expression,即表达式. 通过Expression可以动态构造代码,并编译执行.  比如: 1.  创建参数表达式 :ParameterExpression numParam = Ex ...

  5. 表达式树(Expression Tree)

    你每创建一个表示表达式的实例时,都可以将该类型实例看成是一棵表达式树.每种表示表达式的类型都有一个具体的类型,如Expression的Variable()方法创建的是ParameterExpressi ...

  6. [C#] C# 知识回顾 - 表达式树 Expression Trees

    C# 知识回顾 - 表达式树 Expression Trees 目录 简介 Lambda 表达式创建表达式树 API 创建表达式树 解析表达式树 表达式树的永久性 编译表达式树 执行表达式树 修改表达 ...

  7. 表达式树(Expression Tree)

    饮水思源 本文并非原创而是下面网址的一个学习笔记 https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/e ...

  8. C# 知识回顾 - 表达式树 Expression Trees

    C# 知识回顾 - 表达式树 Expression Trees 目录 简介 Lambda 表达式创建表达式树 API 创建表达式树 解析表达式树 表达式树的永久性 编译表达式树 执行表达式树 修改表达 ...

  9. 【C#表达式树 开篇】 Expression Tree - 动态语言

    .NET 3.5中新增的表达式树(Expression Tree)特性,第一次在.NET平台中引入了"逻辑即数据"的概念.也就是说,我们可以在代码里使用高级语言的形式编写一段逻辑, ...

随机推荐

  1. CF271D 【Good Substrings】

    定义哈希函数 \(H(c)=\sum_{i = 1} ^ m c_i*b^{m-i}\) \(H(C,K+1)=H(C,K)*b+C_{K+1}\)(K个坏字母) 用long long #includ ...

  2. js实现一个长页面中的图片懒加载即滚动到其位置才加载

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  3. Docker 介绍及基础命令

    Docker 简介 Docker 是一个开源项目,诞生于 2013 年初,最初是 dotCloud 公司内部的一个业余项目.它基于 Google 公司推出的 Go 语言实现. 项目后来加入了 Linu ...

  4. java 键盘录入(Scanner)

    键盘录入(Scanner)• 键盘录入数据概述– 我们目前在写程序的时候, 数据值都是固定的, 但是实际开发中, 数据值肯定是变化的, 所以, 把数据改进为键盘录入, 提高程序的灵活性.• 如何实现键 ...

  5. 最大似然估计与期望最大化(EM)算法

    一.最大似然估计与最大后验概率 1.概率与统计 概率与统计是两个不同的概念. 概率是指:模型参数已知,X未知,p(x1) ... p(xn) 都是对应的xi的概率 统计是指:模型参数未知,X已知,根据 ...

  6. 贝叶斯推断 && 概率编程初探

    1. 写在之前的话 0x1:贝叶斯推断的思想 我们从一个例子开始我们本文的讨论.小明是一个编程老手,但是依然坚信bug仍有可能在代码中存在.于是,在实现了一段特别难的算法之后,他开始决定先来一个简单的 ...

  7. 条件随机场(conditional random field,CRF)模型初探

    0. 引言 0x1:为什么会有条件随机场?它解决了什么问题? 在开始学习CRF条件随机场之前,我们需要先了解一下这个算法的来龙去脉,它是在什么情况下被提出的,是从哪个算法演进而来的,它又解决了哪些问题 ...

  8. 010-2 Socket套接字类型

    ocket套接字类型 成员名称 说明 Dgram 支持数据报,即为固定 (通常很小) 的最大长度的无连接的. 不可靠的消息. 消息可能会丢失或重复,并且可能不按顺序抵达. 一个 Socket 类型的  ...

  9. Python——使用高德API获取指定城指定类别POI并实现XLSX文件合并

    # 以下内容为原创,转载请注明出处 1 import xlrd # 读xlsx import xlsxwriter # 写xlsx import urllib.request # url请求,Pyth ...

  10. Kindle复活记

    此前,2015年为了配合拆机堂的内容项目,我们将全新Kindle PaperWhite 3进行全球首拆,让网友们第一时间全面了解了Kindle PaperWhite 3的内部构造.但由于进行深度拆解, ...