我似乎无法在c#中围绕Lambdas

时间:2010-09-15 03:11:09

标签: c# lambda

我还没有真正掌握创建和使用lambda表达式的方法。我知道如何在linq语句中使用它们,但我真的不明白幕后发生了什么。我还没能找到关于何时使用它们,如何定义它们等的完整教程。

第二部分......

他们说Javascript是一种LAMBDA语言,我对javascript非常了解,只是想知道哪些类型的概念适用于javascript lambdas和c#lambdas。

第三部分......

函数式语言和lambda语言有什么区别?

有什么建议吗?

9 个答案:

答案 0 :(得分: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#中,新创建的委托对象跟踪具有变量状态的locals类。在JScript中,新创建的函数对象具有对创建闭包的函数的激活框的引用,这基本上是相同的信息。

lambda被转换为表达式树的方式是相当不同的,但这至少应该让你开始理解lambdas的想法。

答案 1 :(得分:5)

我不能回答第三部分,但让我在1& 2,也许这将帮助你3。

你了解代表吗?因为那真的是你所要处理的。 VS2010 documentation非常简洁。

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

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

就阅读lambda而言,我不喜欢某些人使用的“转到”术语。我倾向于像普通方法一样阅读它,也就是说:

“给定一些参数列表,执行此代码”

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

也许我过于简单了,但我是一个简单的人。

答案 2 :(得分:4)

我可以解决JavaScript部分问题。因为您可以在JS(var fn = function(){/* stuff */};中声明匿名函数,所以您也可以将这些函数作为参数传递给其他函数。事实上,如果您不得不进行自定义排序例程,那么您已经使用过lambdas。例如:

// 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函数作为参数的例子。

在您自己的代码中执行此操作非常简单,但在实践中我从未发现无法完成的情况更清楚其他方式(如果其他人需要管理您的代码,你保证在他们看到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对象即可获得所需的输出。由于存在范围问题,您必须跳过一些箍来将对象中的变量转换为发布方法,但是一旦这样做,其余的就很容易了。

我知道还有其他人可以提供更好的例子,但我希望这对你有所帮助。

答案 3 :(得分:0)

答案 4 :(得分:0)

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

答案 5 :(得分:0)

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

答案 6 :(得分:0)

Lambda本身并不是一种“语言”,就像它是C#语言的一部分一样。 http://msdn.microsoft.com/en-us/library/bb397687(VS.90).aspx

Lambada表达式是“合成糖”,可以消除大量冗余编码。

http://www.rvenables.com/tag/lambda-expressions/有一个很好的例子,说明了lambada提供的快捷方式。

http://www.codeproject.com/KB/cs/DelegatesOMy.aspx有另一个前/后lambda的例子。

答案 7 :(得分: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&amp; inches2Centimetres是函数名称的大小写。但是对于代码清洁度和可维护性,lambda的使用给了我更好的封装。

现在由于函数被定义为Func<...>类型的变量,你可以像变量一样传递lambdas。

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

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

在这种情况下,当我调用函数时,Func<IDataReader, T> transform可以传入lamdba,它只需要知道如何将当前行转换为T类型的对象。所有用于打开连接,执行reader命令,在记录之间移动以及干净地关闭所有内容的代码都由扩展方法巧妙地处理。我的通话代码更加简洁。

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

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

答案 8 :(得分:0)

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

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

将'Lambda'视为一个接受参数并返回值的函数。