2010-09-15 63 views
3

我还没有真正能够牢牢把握创建和使用lambda表达式。我知道如何在linq语句中使用它们,但我真的不明白幕后发生了什么。我也还没有能够找到关于何时使用它们完整的教程,如何去界定等我似乎无法让我的头在c#中的Lambdas附近#

第二部分...

他们说,JavaScript是一种语言LAMBDA,我知道的JavaScript相当好,只是想知道什么类型的概念适用于JavaScript lambda和c#lambda。

THRID部分...

就是一个功能性的语言和语言拉姆达之间的区别?

有什么建议吗?

+1

您可能被命名吓倒了。如果你知道数学中有什么函数(比如x-> x^2),那么你就已经了解了lambda。 – greenoldman 2010-09-15 06:50:45

回答

8

让我来告诉你后台发生了什么事。它比你想象的更直接。

假设您有:

delegate int D(int x); 
... 
class C 
{ 
    void M(int y) 
    { 
     int z = 123; 
     D d = x=>x+y+z; 
     Console.WriteLine(d(10)); 
     z = 345; 
     y = 789; 
     Console.WriteLine(d(10)); 
    } 
} 

所有的编译器是假装你写道:

delegate int D(int x); 
... 
class C 
{ 
    private class Locals 
    { 
     public int y; 
     public int z; 
     public int A(int x) 
     { 
      return x + this.y + this.z; 
     } 
    } 
    void M(int y) 
    { 
     // Initialize the closure class: 

     Locals locals = new Locals(); 
     locals.y = y; 

     // Transform the body so that all usages of y, z and the lambda 
     // refer to the closure class: 

     locals.z = 123; 
     D d = locals.A; 
     Console.WriteLine(d(10)); // Calls locals.A(10) 
     locals.z = 345; 
     locals.y = 789; 
     Console.WriteLine(d(10)); // Calls locals.A(10) 

    } 
} 

这一切就是这么简单。 lambda只是一个紧凑的语法,用于编写“将lambda使用的所有外部局部变量提升到一个类中,使用给定的主体对该类进行方法,并使我成为该方法的代表”。

JScript中的函数闭包的工作方式基本相同。JScript当然不是一个基于类的语言,所以细节略有不同,但这个想法是一样的。在C#中,新创建的委托对象跟踪具有变量状态的局部类。在JScript中,新创建的函数对象具有对创建闭包的函数的激活框架的引用,它基本上是相同的信息。

将lambda表达式转换为表达式树的方式相当不同,但这至少应该让您开始了解lambda表达式的概念。

+0

在'locals locals = new Locals();'和'locals.z = z;'上面的一行代替'locals.z = 123'代替'int z = 123;'会更准确吗? ''你的演示代码似乎有点偏离,因为在'M'后面的lambda之外的地方使用了z。或者,也许我的建议让事情变得更糟,因为如果y或z在代表声明和writeline之间被修改。 – Brian 2010-09-15 13:51:09

+3

@布莱恩:它不是*更精确*,它*不正确*。闭包捕获*整个变量*,而不是闭包创建时的值*。没有更多的局部变量z;它完全消失*。如果z在M中的lambda之外的某处使用,那么z也会变成locals.z。 *由lambda转换为z的状态在lambda *之外是可见的。再次,让我重复一遍以确保它清晰:封闭捕获**变量**,而不是**其值**。很多人对此感到困惑。 – 2010-09-15 14:21:24

+1

@Eric Lippert:那么不是'locals.y = y;'不正确?是的,我意识到语法不允许你说'locals.y是y的别名;' – Brian 2010-09-15 14:43:34

5

我不能回答三部分,但让我采取摇摆在1 & 2,也许,这将有助于你与3

你懂的代表?因为这就是你正在处理的一切。 VS2010 documentation非常简洁。

在根中,lambda只是传递给委托声明的匿名函数。 或者,更简单的方法没有签名(返回类型,名称和参数)。该用法暗示签名。

询问“何时使用lambda”实际上提出了“何时应该为委托使用匿名函数”的问题,实际上,我想不出比LINQ使用它们更好的场景作为示例。 AFAIK和理解,JavaScript是一种lambda语言,因为你可以像变量一样传递方法,并且使用匿名方法来传递方法,而不仅仅是声明的方法。

就阅读拉姆达而言,我不喜欢某些人使用的“去”术语。我倾向于像一个正常的方法,这是说来阅读:

“给出的参数列表部分,执行该代码”

所以, 排=> row.State ==“NY” 手段 “给定行,当行的状态为纽约时返回true”。

也许我太简单了,但我是一个简单的家伙。

+0

完全符合您阅读lambda的方法。这个“去”的东西听起来很愚蠢。 – Dave 2010-09-15 04:01:50

0

简介中的C#3以及Jon Skeet的书在他们的书中很好地解决了这个问题。

0

lambda表达式是一个匿名的,一流的函数。当你声明一个,你创建一个可寻址的函数,但可以有任意数量的别名,就像一个对象(因此,第一类)。你可以在变量和参数中传递它。 Javascript可以称为lambda语言,因为所有的javascript函数都具有这些属性。它们可以被实例化,别名和通过,并且不需要像C do中的函数那样具有名称。所以,他们是匿名的,一流的功能。

+0

所以主要区别,正如我所看到的那样,javascript中的lambdas本身可以具有属性,而在c#中则不会。那是对的吗? – 2010-09-15 15:19:27

+0

这是正确的,但我不会说它是主要区别。这种差异主要是动态(Javascript)与静态(C#)输入的副作用。当你在C#中使用lambda时,它的类型完全由它的签名来定义。 JavaScript中的匿名方法也是如此,除了在Javascript中,您可以在实例化后为其生成的对象添加属性。另外,在Javascript中,你会注意到没有真正的签名,并且没有类型约束 - 它是鸭子类型的。 – 2010-09-15 17:11:58

0

我会在你的问题的第一部分。可能值得重新发布第二部分作为单独的问题。

我喜欢将lambdas作为我定义的函数/过程,而不是模块级别来定义。

所以,让我们说我有这样的代码:

public void Run() 
{ 
    var template = "{0} inches is {1} centimetres."; 
    Console.WriteLine(template, 2.0, 2.0 * 2.54); 
    Console.WriteLine(template, 5.0, 5.0 * 2.54); 
} 

显然,我不想重复表达* 2.54这样我就可以这样定义一个函数:

private double Inches2Centimetres(double inches) 
{ 
    return inches * 2.54; 
} 

而且我的代码现在变成:

public void Run() 
{ 
    var template = "{0} inches is {1} centimetres."; 
    Console.WriteLine(template, 2.0, Inches2Centimetres(2.0)); 
    Console.WriteLine(template, 5.0, Inches2Centimetres(5.0)); 
} 

但是这个功能与调用代码分开,如果这是我需要计算的唯一地方,我实际上正在伤害我的代码的可维护性。

我可以决定使用lambda,这样我就可以代替这样写:

public void Run() 
{ 
    Func<double, double> inches2Centimetres = inches => inches * 2.54; 

    var template = "{0} inches is {1} centimetres."; 
    Console.WriteLine(template, 2.0, inches2Centimetres(2.0)); 
    Console.WriteLine(template, 5.0, inches2Centimetres(5.0)); 
} 

至于我的调用代码而言,Inches2Centimetres & inches2Centimetres之间的区别是函数名的外壳。但是对于代码清洁和可维护性来说,lambda的使用给了我更好的封装。

现在,由于函数被定义为Func<...>类型的变量,所以您可以像变量一样传递lambda表达式。

下面是一个很好的例子,它可以是有用的:我会经常创建类似数据库连接的扩展方法来隐藏管道gumpf,从而留下我正在编写的代码的本质。

public static T[] UsingDataReader<T>(
    this IDbConnection @this, 
    string query, 
    Func<IDataReader, T> transform) 
{ 
    //... 
} 

在这种情况下lamdba,Func<IDataReader, T> transform然后可以当我调用该函数传入,而且只需要知道如何把当前行到T类型的对象。所有用于打开连接,执行阅读器命令,在记录之间移动以及干净地关闭所有内容的代码都由扩展方法处理。我的调用代码要简洁得多。

string[] names = conn.UsingDataReader<string>("select FirstName from People;", 
    dr => dr["FirstName"] as string); 

我知道我已经提供了一个非常简单的答案给你的问题,但我希望它有帮助。

0

传统Lambda演算中的函数是一个函数,它对单个值进行操作并返回一个单值 - 一元函数。它们也是非常简单的功能,没有做任何特别复杂的事情(通常将一组映射到另一组)。为了做复杂的工作,你可以链接函数,这样第一个函数的输出就是第二个函数的输入。

在C#中的Lambdas只是一样的函数。我们使用它们来投影,转换,过滤或排序数据。

将'Lambda'看作是一个函数,它接受参数并返回一个值。

4

我可以解决JavaScript部分。因为你可以在JS(var fn = function(){/* stuff */};)中声明匿名函数,所以你也可以将这些函数作为参数传递给其他函数,事实上,如果你需要做一个自定义的排序例程,那么你已经使用了lambda表达式,例如:

// Standard sort: 
x = [4,3,6,7,1,5,2]; 
x.sort(); 

// Custom sort: 
y = [ 
    {'val':4,'name':'four'}, 
    {'val':3,'name':'three'}, 
    {'val':6,'name':'six'}, 
    {'val':7,'name':'seven'}, 
    {'val':1,'name':'one'}, 
    {'val':5,'name':'five'}, 
    {'val':2,'name':'two'}, 
]; 
y.sort(function(a,b){ return a.val > b.val ? 1 : -1 }); 

replace()是另一个例子,采取了lambda函数作为参数。

在自己的代码这样做是很容易的,但实际上我从来没有发现一个情况,当这不可能做到更清楚一些其他方式(如果其他人需要管理你的代码,你必须打破他们的头,直到他们看到拉姆达)

下面是一个示例。假设你有一个产生某种形式的输出的对象Widget。你知道它总是会产生输出,但是你不知道输出的结果是什么。一种解决方案是将对象生成输出所需的方法传递给对象。这里有一个简单的实现:

首先,Widget本身。需要注意的是Widget.prototype.publish()只有一个参数,这是你的自定义格式:

var Widget = function() { 
    var self = this; 
    var strPrivateVar = "This is a private variable"; 
    self.publicVar = "This is a default public variable"; 
    self.publish = function(f) { 
     var fnFormatter = f; 
     var strOutput = "The output is " + fnFormatter(self,strPrivateVar); 
     return strOutput; 
    } 
}; 

接下来,你的格式化。一个简要总结,而另一个给人的全文:

var fnSummary = function(o,s) { 
    var self = o; 
    var strPrivateVar = s; 
    return strPrivateVar.substr(0,5) + ' ' + self.publicVar.substr(0,5); 
} 
var fnDetails = function(o,s) { 
    var self = o; 
    var strPrivateVar = s; 
    return strPrivateVar + ' ' + self.publicVar; 
} 

而在去年,您的实现:

var wWidget = new Widget(); 
wWidget.publicVar = "I have overridden the public property"; 
var strSummary = wWidget.publish(fnSummary); 
var strDetails = wWidget.publish(fnDetails); 
console.log(strSummary,strDetails); 

这种解决方案意味着你不需要改变wWidget对象,以获得所需输出。由于范围问题,您必须跳过一些环节才能将对象中的变量转换为发布方法,但一旦完成了这些操作,其余的操作就很容易了。

我知道还有其他人可以举一个更好的例子,但我希望这可以帮助你。

相关问题