【问题标题】:What is this 'Lambda' everyone keeps speaking of?每个人都在谈论的这个“Lambda”是什么?
【发布时间】:2010-11-08 07:46:10
【问题描述】:

每个人都在谈论的“Lambda”是什么?很多人似乎都喜欢它,但我能从中得到的只是它只是将大量代码行塞进一个表达式中的一种方式。

有人能告诉我它的真正价值吗?

【问题讨论】:

标签: lambda


【解决方案1】:

没有名字的函数

简单地说,lambda 是一个没有名字的函数,或者是一个匿名函数。一小段可执行代码,可以像变量一样传递。在 JavaScript 中:

function () {}; // very simple

现在让我们看看这些 lambda 的一些用途。

抽象样板代码

Lambda 可用于抽象出样板代码。例如循环。我们习惯于整天编写forwhile 循环。但这是不写的代码。我们可以提取循环内的代码,这是循环中最重要的部分,并抽象出其余部分:

for (var i=0; i<array.length; i++) {
    // do what something useful with array[i]
}

通过使用数组对象的forEach,变为:

array.forEach(function (element, index) {
   // do something useful with element
   // element is the equivalent of array[i] from above
});

上面的抽象可能没有那么有用,但是还有其他更高阶的函数,比如forEach,可以执行更有用的任务。例如filter:

var numbers = [1, 2, 3, 4];
var even    = [];

// keep all even numbers from above array
for (var i=0; i<numbers.length; i++) {
    if (numbers[i] % 2 === 0) {
        even.push(numbers[i]);
    }
}

alert(even);

// Using the filter method
even = [1, 2, 3, 4].filter(function (number) {
    return number % 2 === 0;
});

alert(even);

代码执行延迟

在某些可以使用事件概念的环境中,我们可以使用 lambdas 来响应可能在某个时间点发生的事件。

window.onload = function () {
    alert("Loaded");
};

window.setTimeout(function () {
    alert("Code executed after 2 seconds.");
}, 2000);

这可以通过其他方式完成,但这些方式相当冗长。例如,在 Java 中有 Runnable 接口。

函数工厂

到目前为止,我们主要使用 lambdas 来实现其语法糖功能。但在某些情况下,lambda 可能更有用。例如,我们可能有返回 lambdas 的函数。假设我们有一个函数,我们希望缓存它的返回值。

var users = [];
var getUser = function (name) {
    if (! users[name]) {
        // expensive operations to get a user. Ajax for example
        users[name] = user_from_ajax;
    }

    return users[name];
};

稍后,我们可能会注意到我们有类似的功能:

var photos = [];
var getPhoto = function (name) {
    if (! photo[name]) {
        // expensive operations to get a user. Ajax for example
        photos[name] = photo_from_ajax;
    }

    return photos[name];
};

其中显然有一个模式,所以让我们将其抽象出来。让我们使用memoization

/**
 * @param {Array}     store Data structure in which we cache lambda's return values
 * @param {Function}  lambda
 * @return {Function} A function that caches the result of calling the lambda param
 */
var memoize = function (store, lambda) {
    // return a new lambda
    return function (name) {
        if (! store[name]) {
            // Execute the lambda and cache the result
            store[name] = lambda(name);
        }

        return store[name];
    };
};

var getUsers = memoize([], function (name) {
    // expensive operations to get a user. Ajax for example
});

var getPhotos = memoize([], function (name) {
    // expensive operations to get a photo. Ajax for example
});

如您所见,通过使用 lambda,我们能够抽象出缓存/记忆逻辑。如果对于另一个示例有一些解决方法,我相信使用其他技术很难解决这个特定问题。我们设法将一些重要的样板代码提取到一个地方。更不用说我们去掉了usersphotos 全局变量。

查看您的个人资料,我发现您主要是 Python 用户。对于上述模式,Python 有装饰器的概念。有很多 example on the net for memoization 装饰器。唯一的区别是,在 Python 中,您很可能在装饰器函数中有一个 named 嵌套函数。原因是 Python 只支持单表达式 lambda。但概念是一样的。

作为 Python lambda 使用的示例。上面我们过滤偶数的代码在 Python 中可以这样表示:

filter(lambda x: x % 2 == 0, [1, 2, 3, 4])

无论如何,如果没有闭包,lambda 就不会那么强大。闭包是 lambda 概念如此强大的原因。在我的记忆示例中,我使用闭包围绕store 参数创建闭包。这样,即使memoize 函数返回了它的结果(一个 lambda),我也可以访问该参数。

【讨论】:

  • 哇,你花了很多时间。
  • @Mk12,在实际编写答案时,并非如此。在学习这些东西时,是的,我已经有一段时间没有开始了:)
  • 很好的答案,但缺少有关“功能接口”的信息(从 Java 的角度来看)。
  • “抽象样板代码”中的那些“===”运算符是什么?
【解决方案2】:

术语“lambda”用于指代匿名函数,通常是closure。它们很有用,因为它们允许您编写使用其他函数的函数,而不会不必要地膨胀您的代码。例如,在 Ruby 中:

(1..100).select {|num| num % 2 == 0}

这将创建一个包含 1 到 100 之间的偶数的数组。我们不必编写显式循环 - select 方法采用一个用于测试值的函数,因此我们只需要自定义逻辑。这使我们能够在几乎不费力或开销的情况下极大地定制该方法。基本上,我们可以用更小的函数来组合函数。

这只是他们可以做什么的一个简单示例。将函数作为数据传递的能力非常强大,函数式语言程序员经常用它来做一些非常了不起的事情。

【讨论】:

  • 也许你应该补充一点,管道内的东西是参数。我是这些阅读红宝石有困难的人之一。
  • 这是一个很好的答案。 Ionut 获得我投票的唯一原因是他告诉我们为什么我们应该(详细地)关心 lambda。
  • 我不同意 lambda 通常是闭包。
【解决方案3】:

“Lambda”可能太少了。看看Lambda calculus。 它在函数式编程中很有用。

函数式编程是另一种编程范式(如过程式或面向对象)。

【讨论】:

  • 然后人们谈论“Lambda”,很可能他们在谈论匿名函数、函数指针、闭包或类似的东西。几乎从不提及真正的 lambda 演算。
  • 很高兴您提到了 Lambda 演算! +1。
【解决方案4】:

.NET 中的 Lambda 通常被称为“语法糖”。它们不直接影响功能,但它们使语言更易于人们使用。

当您了解使用它们的强大功能后,我相信您会发现与使用委托/匿名方法的旧式方式相比,您将编写更少的代码。

【讨论】:

  • 我认为没有人提到 .NET,所以 OP 可能最好提供更一般的答案。
  • 这就是为什么我通过回答是关于 .net 来澄清的。如果其他人对他们的语言实现赞不绝口,那么无论他们选择哪种语言,问答都会对许多人有所帮助。
  • 这个答案读起来就像你听说过一些关于 lambdas 的东西,但你自己还不了解它们。
【解决方案5】:

Dr Dobbs Journal 有一个有用的article introducing lambda expressions(在 C++ 的上下文中,但我认为您可以将这些原则应用于任何语言)。

正如文章所说:“lambda 表达式是一种高度紧凑的表达式,不需要单独的类/函数定义。”

所以使用来自DDJ 的清单 1 和 2 的示例,而不是写作:

std::for_each( vec.begin(), vec.end(), print_to_stream<std::string>(std::cout));

这需要一个单独的类定义,例如:

template <typename T, typename Stream> class print_to_stream_t {
  Stream& stream_;
public:
  print_to_stream_t(Stream& s):stream_(s) {}
  void operator()(const T& t) const {
    stream_ << t;
  }
};
template <typename T,typename Stream> 
print_to_stream_t<T,Stream>   print_to_stream(Stream& s) {
  return print_to_stream_t<T,Stream>(s);
}

使用Boost lambda library可以变成:

std::for_each(vec.begin(),vec.end(),std::cout << _1);

使定义保持内联。

The article 还解释了 lambda 表达式的更多应用。

我认为the DDJ article 中的一个关键点是“通常情况下,当调用站点需要小型且不太复杂的函数时,会使用 lambda 表达式。如果该函数不平凡,您将不需要 lambda 表达式,而是普通函数或函数对象。”

【讨论】:

    【解决方案6】:

    如果您曾经使用过使用函数指针、委托、策略或观察者模式/事件处理的函数/方法,并且认为“我编写整个函数只是为了使用它一次 - 传递它到这个方法;我希望我能把它写在适当的位置,而不是把我的代码弄得乱七八糟”——这就是你可以使用 Lambda 函数的地方。支持这种结构的语言通常也大量利用将函数作为参数传递的概念,特别是在处理列表(一等函数和高阶函数)方面。对于函数式语言来说尤其如此,它依赖于函数组合而不是内存修改来进行计算。在某些情况下(在 Python 等语言中),使用带有列表解析的 lambda 函数也被认为比等效的 foreach 循环更具可读性。

    【讨论】:

      【解决方案7】:

      'lambda' qua word 是计算机科学类型在数学或逻辑方面接受培训的可能性比拥有计算机科学学位时的术语。他们中的一些人提出了一种称为“函数式编程”的范式,与命令式编程完全不同,也非常强大。 AFAIK 是该术语开始使用的环境。

      数学家和逻辑学家习惯于使用奇怪的词。

      'lambda' 听起来很深奥——好像它们是一个非常奇怪和特殊的东西。确实,如果您为 Web 浏览器应用程序编写 JavaScript 并使用“var foo=function() {...}”习语,那么您一直在使用 lambda 函数。

      【讨论】:

        【解决方案8】:

        lambda 表达式是一种简单的函数形式。这个想法是左边的某种形式(相当于参数)变成右边的某种形式(相当于主体)。

        例如,在升c中:

        x => x * x
        

        是一个求平方值的 lambda。某种形式的东西

        x
        

        变成某种形式

        x * x
        

        【讨论】:

          【解决方案9】:

          “lambda 表达式是一个匿名函数,可以包含表达式和语句,可用于创建委托或表达式树类型。

          所有 lambda 表达式都使用 lambda 运算符 =>,读作“goes to”。 lambda 运算符的左侧指定输入参数(如果有),右侧保存表达式或语句块。 lambda 表达式 x => x * x 读作“x 达到 x 乘以 x”。

          来自MSDN

          【讨论】:

          • 是的,微软让每个人都认为它是他们发明的。不过,Lambda 表达式早于微软。这是一个数学术语,已应用于多种编程语言。 (这是可能的,因为数学本身可以被视为一种计算机语言。)
          • 请注意,这是特定于 Microsoft 的 .Net 实现的。这与 lambda 的一般概念并没有太大的偏差,但我认为在 Lisp 中实现的功能更“标准”。
          • 这个答案根本没有解释 Lambda 是什么(需要定义匿名函数、委托、表达式树类型),当然也没有解释它的值是什么。
          【解决方案10】:

          有关 Lambda 表达式的完整说明,另请查看 Wikipedia。 (向下滚动到 Lambda 演算和编程语言 部分。)Lambda 表达式并不是那么新,它们不仅仅是 C# 的一部分,而是近 80 年前引入计算的东西! Lambda 表达式是函数式编程的基础。

          它的价值?好吧,考虑到它实际上已经很老了,我想说:对于任何进行计算的人来说都非常有价值。

          【讨论】:

            【解决方案11】:

            如果您对 Java 感兴趣,那么在过去的几个月中,您会听到很多关于 lambda 或闭包的消息,因为对于将这个功能添加到 Java 7 中存在不同的建议。但是,我认为委员会放弃了它。其中一项提案来自 Neal Gafter,并在此处进行了详细解释:javac.info。这有助于我理解用例和优势(尤其是内部类)

            【讨论】:

              【解决方案12】:

              您可以在这里找到所有需要了解的信息(关于 C# Lambda):
              Lambda expressions

              【讨论】:

                【解决方案13】:

                对于 java lambdas,这可能是一个很好的起点:

                http://rodrigouchoa.wordpress.com/2014/09/10/java-8-lambda-expressions-tutorial/

                【讨论】:

                  【解决方案14】:

                  是的,它只是将大量代码行塞进一个表达式中的一种方式。但是,作为一种高效的临时填鸭式,它可以使用一些新的方式来构建您的程序。

                  通常人们会避免编写委托或回调并恢复为过程式风格,仅仅是因为为单个表达式声明新函数或类的工作量太大。

                  Lambda 表达式使得即使对于最小的任务也值得使用回调,这可能会使代码更清晰。可能不会。

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2016-06-05
                    • 1970-01-01
                    • 2012-09-06
                    • 2015-06-12
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多