什么是lambda(函数)?

时间:2008-08-19 16:20:38

标签: lambda language-agnostic computer-science terminology theory

对于没有comp-sci背景的人来说,计算机科学界的lambda是什么?

24 个答案:

答案 0 :(得分:1010)

Lambda来自Lambda Calculus,指的是编程中的匿名函数。

为什么这很酷?它允许您在不命名的情况下编写快速丢弃功能。它还提供了一种编写闭包的好方法。有了这种力量,你就可以做到这样的事情。

<强>的Python

def adder(x):
    return lambda y: x + y
add5 = adder(5)
add5(1)
6

从Python的片段中可以看出,函数加法器接受参数x,并返回一个带有另一个参数y的匿名函数或lambda。该匿名函数允许您从函数创建函数。这是一个简单的例子,但它应该传达lambda和闭包的力量。

其他语言的示例

Perl 5

sub adder {
    my ($x) = @_;
    return sub {
        my ($y) = @_;
        $x + $y
    }
}

my $add5 = adder(5);
print &$add5(1) == 6 ? "ok\n" : "not ok\n";

<强>的JavaScript

var adder = function (x) {
    return function (y) {
        return x + y;
    };
};
add5 = adder(5);
add5(1) == 6

JavaScript(ES6)

const adder = x => y => x + y;
add5 = adder(5);
add5(1) == 6

<强>方案

(define adder
    (lambda (x)
        (lambda (y)
           (+ x y))))
(define add5
    (adder 5))
(add5 1)
6

<强> C# 3.5 or higher

Func<int, Func<int, int>> adder = 
    (int x) => (int y) => x + y; // `int` declarations optional
Func<int, int> add5 = adder(5);
var add6 = adder(6); // Using implicit typing
Debug.Assert(add5(1) == 6);
Debug.Assert(add6(-1) == 5);

// Closure example
int yEnclosed = 1;
Func<int, int> addWithClosure = 
    (x) => x + yEnclosed;
Debug.Assert(addWithClosure(2) == 3);

<强>夫特

func adder(x: Int) -> (Int) -> Int{
   return { y in x + y }
}
let add5 = adder(5)
add5(1)
6

<强> PHP

$a = 1;
$b = 2;

$lambda = function () use (&$a, &$b) {
    echo $a + $b;
};

echo $lambda();

<强>的Haskell

(\x y -> x + y) 

Java ,请参阅this post

// The following is an example of Predicate : 
// a functional interface that takes an argument 
// and returns a boolean primitive type.

Predicate<Integer> pred = x -> x % 2 == 0; // Tests if the parameter is even.
boolean result = pred.test(4); // true

<强>的Lua

adder = function(x)
    return function(y)
        return x + y
    end
end
add5 = adder(5)
add5(1) == 6        -- true

<强>科特林

val pred = { x: Int -> x % 2 == 0 }
val result = pred(4) // true

<强>红宝石

Ruby略有不同,因为你不能使用与调用函数完全相同的语法来调用lambda,但它仍然有lambdas。

def adder(x)
  lambda { |y| x + y }
end
add5 = adder(5)
add5[1] == 6

Ruby是Ruby,有一个lambdas的简写,所以你可以这样定义adder

def adder(x)
  -> y { x + y }
end

答案 1 :(得分:101)

lambda是一种函数,内联定义。除了lambda之外,你通常还有一些变量类型可以保存对函数,lambda或其他函数的引用。

例如,这是一段不使用lambda的C#代码:

public Int32 Add(Int32 a, Int32 b)
{
    return a + b;
}

public Int32 Sub(Int32 a, Int32 b)
{
    return a - b;
}

public delegate Int32 Op(Int32 a, Int32 b);

public void Calculator(Int32 a, Int32 b, Op op)
{
    Console.WriteLine("Calculator: op(" + a + ", " + b + ") = " + op(a, b));
}

public void Test()
{
    Calculator(10, 23, Add);
    Calculator(10, 23, Sub);
}

这会调用计算器,不仅传递两个数字,而且调用Calculator内部的方法来获取计算结果。

在C#2.0中,我们获得了匿名方法,将上述代码缩短为:

public delegate Int32 Op(Int32 a, Int32 b);

public void Calculator(Int32 a, Int32 b, Op op)
{
    Console.WriteLine("Calculator: op(" + a + ", " + b + ") = " + op(a, b));
}

public void Test()
{
    Calculator(10, 23, delegate(Int32 a, Int32 b)
    {
        return a + b;
    });
    Calculator(10, 23, delegate(Int32 a, Int32 b)
    {
        return a - b;
    });
}

然后在C#3.0中我们得到了lambdas,这使代码更短:

public delegate Int32 Op(Int32 a, Int32 b);

public void Calculator(Int32 a, Int32 b, Op op)
{
    Console.WriteLine("Calculator: op(" + a + ", " + b + ") = " + op(a, b));
}

public void Test()
{
    Calculator(10, 23, (a, b) => a + b);
    Calculator(10, 23, (a, b) => a - b);
}

答案 2 :(得分:60)

它指的是lambda calculus,它是一个只有lambda表达式的正式系统,它表示一个函数,它为其唯一的参数获取一个函数并返回一个函数。 lambda演算中的所有函数都属于该类型,即λ : λ → λ

Lisp使用lambda概念命名其匿名函数文字。这个lambda代表一个函数,它接受两个参数x和y,并返回它们的乘积:

(lambda (x y) (* x y)) 

它可以像这样在线应用(评估为 50 ):

((lambda (x y) (* x y)) 5 10)

答案 3 :(得分:56)

名称“lambda”只是一个历史文物。我们所谈论的只是一个表达式,其价值是一个函数。

一个简单的例子(使用Scala作为下一行)是:

args.foreach(arg => println(arg))

其中foreach方法的参数是匿名函数的表达式。上面这行与写这样的东西大致相同(不是真正的代码,但你会明白的):

void printThat(Object that) {
  println(that)
}
...
args.foreach(printThat)

除了你不需要打扰:

  1. 在其他地方声明该功能(并在以后重新访问代码时必须查找它)。
  2. 命名您只使用过一次的内容。
  3. 一旦习惯了函数值,不必使用它们就像为每个表达式命名一样愚蠢,例如:

    int tempVar = 2 * a + b
    ...
    println(tempVar)
    

    而不是仅仅根据需要编写表达式:

    println(2 * a + b)
    

    确切的符号因语言而异;希腊语并不总是必需的! ; - )

答案 4 :(得分:45)

lambda演算是一种一致的数学替代理论。在学校数学中,人们可以看到x+y=5x−y=1配对。除了操纵单个方程的方法之外,还可以将来自这两个方程的信息放在一起,只要逻辑上完成交叉方程替换。 Lambda演算编写了进行这些替换的正确方法。

鉴于y = x−1是第二个等式的有效重新排列,这个:λ y = x−1表示用符号x−1代替符号y的函数。现在想象一下将λ y应用于第一个等式中的每个项。如果一个术语是y,那么执行替换;否则什么也不做。如果您在纸上执行此操作,您将看到应用λ y将如何使第一个等式可解决。

这是一个没有任何计算机科学或编程的答案。

我能想到的最简单的编程示例来自http://en.wikipedia.org/wiki/Joy_(programming_language)#How_it_works

  

这里是如何在命令中定义平方函数   编程语言(C):

int square(int x)
{
    return x * x;
}
     

变量x是形式参数,由实际替换   调用函数时要平方的值。在功能上   语言(Scheme)将定义相同的函数:

(define square
  (lambda (x) 
    (* x x)))
     

这在许多方面有所不同,但它仍然使用形式参数   x以同样的方式。


已添加: http://imgur.com/a/XBHub

lambda

答案 5 :(得分:14)

稍微过度简化:lambda函数是一个可以传递给其他函数并且可以逻辑访问的函数。

在C#中,lambda语法通常以与匿名委托相同的方式编译为简单方法,但也可以将其分解并读取其逻辑。

例如(在C#3中):

LinqToSqlContext.Where( 
    row => row.FieldName > 15 );

LinqToSql可以读取该函数(x> 15)并将其转换为实际的SQL以使用表达式树执行。

上述陈述变为:

select ... from [tablename] 
where [FieldName] > 15      --this line was 'read' from the lambda function

这与普通方法或匿名委托(实际上只是编译魔术)不同,因为它们不能读取

并非C#中使用lambda语法的所有方法都可以编译为表达式树(即实际的lambda函数)。例如:

LinqToSqlContext.Where( 
    row => SomeComplexCheck( row.FieldName ) );

现在无法读取表达式树 - SomeComplexCheck无法分解。 SQL语句将在没有where的情况下执行,数据中的每一行都将通过SomeComplexCheck

Lambda函数不应与匿名方法混淆。例如:

LinqToSqlContext.Where( 
    delegate ( DataRow row ) { 
        return row.FieldName > 15; 
    } );

这也有一个'内联'函数,但这次它只是编译器魔术 - C#编译器会将其拆分为具有自动生成名称的新实例方法。

无法读取匿名方法,因此逻辑无法转换为lambda函数。

答案 6 :(得分:7)

我喜欢本文中对Lambdas的解释:The Evolution Of LINQ And Its Impact On The Design Of C#。这对我来说很有意义,因为它为Lambdas展示了一个真实的世界,并将其作为一个实际的例子来构建。

他们的快速解释:Lambdas是一种将代码(函数)视为数据的方法。

答案 7 :(得分:7)

Ruby中lambda的一个例子如下:

hello = lambda do
    puts('Hello')
    puts('I am inside a proc')
end

hello.call

将生成以下输出:

Hello
I am inside a proc

答案 8 :(得分:5)

这个问题得到了正式的回答,所以我不会尝试在此上添加更多内容。

对于那些对数学或编程知之甚少或根本不知道的人来说,非常简单,非正式的话,我会将其解释为一台小型机器&#34;或&#34;框&#34;需要一些输入,做一些工作并产生一些输出,没有特别的名字,但我们知道它在哪里,只有这些知识,我们使用它。

实际上,对于一个知道函数是什么的人,我会告诉他们这是一个没有名字的函数,通常放在内存中只能通过引用该内存来使用(通常是通过变量的用法 - 如果他们已经听说过函数指针的概念,我会将它们用作类似的概念) - 这个答案涵盖了漂亮的基础知识(没有提到闭包等),但是人们可以很容易地理解这一点。 / p>

答案 9 :(得分:5)

@Brian我一直在C#,LINQ和非LINQ运算符中使用lambdas。例如:

string[] GetCustomerNames(IEnumerable<Customer> customers)
 { return customers.Select(c=>c.Name);
 }

在C#之前,我使用JavaScript中的匿名函数来回调AJAX函数,然后才创造出Ajax这个术语:

getXmlFromServer(function(result) {/*success*/}, function(error){/*fail*/});

C#的lambda语法有趣的是,它们本身的类型不能被推测(即,你不能输入var foo =(x,y)=&gt; x * y)但是取决于哪个他们被分配的类型,它们将被编译为代表表达式的委托或抽象语法树(这是LINQ对象映射器如何进行“语言集成”魔术)。

LISP中的Lambdas也可以传递给报价运算符,然后作为列表列表遍历。一些强大的宏是以这种方式制作的。

答案 10 :(得分:4)

您可以将其视为匿名函数 - 这里有更多信息:Wikipedia - Anonymous Function

答案 11 :(得分:4)

仅仅因为我在这里看不到C ++ 11的例子,我将继续发布这个来自here的好例子。搜索之后,这是我能找到的最清晰的语言特定示例。

您好,Lambdas,版本1

template<typename F>

void Eval( const F& f ) {
        f();
}
void foo() {
        Eval( []{ printf("Hello, Lambdas\n"); } );
}

您好,Lambdas,版本2:

void bar() {
    auto f = []{ printf("Hello, Lambdas\n"); };
    f();
}

答案 12 :(得分:3)

我无法绕过lambda表达式,因为我在Visual FoxPro中工作,它具有宏替换以及ExecScript {}和Evaluate()函数,这些函数似乎具有相同的用途。

? Calculator(10, 23, "a + b")
? Calculator(10, 23, "a - b");

FUNCTION Calculator(a, b, op)
RETURN Evaluate(op)

使用正式lambdas的一个明显好处是(我假设)编译时检查:Fox不会知道你上面的文本字符串是否输入错误,直到它试图运行它。

这对于数据驱动的代码也很有用:您可以将整个例程存储在数据库的备注字段中,然后在运行时对它们进行评估。这使您可以调整部分应用程序,而无需实际访问源。 (但这完全是另一个话题。)

答案 13 :(得分:3)

  

对于没有comp-sci背景的人来说,计算机科学界的lambda是什么?

我将在简单易读的python代码中逐步直观地说明它。

简而言之,lambda只是一个匿名和内联函数。

让我们从作业开始,将lambdas理解为具有基本算术背景的新生。

作业蓝图是'名称=价值',见:

In [1]: x = 1
   ...: y = 'value'
In [2]: x
Out[2]: 1
In [3]: y
Out[3]: 'value'

'x','y'是名称和1,'value'是值。 尝试数学中的函数

In [4]: m = n**2 + 2*n + 1
NameError: name 'n' is not defined

错误报告,
你不能直接把数学写成代码,应该定义'n'或者赋值给它。

In [8]: n = 3.14
In [9]: m = n**2 + 2*n + 1
In [10]: m
Out[10]: 17.1396

它现在有效,如果你坚持将两条单行合并为一条,那该怎么办? 来lambda

In [13]: j = lambda i: i**2 + 2*i + 1
In [14]: j
Out[14]: <function __main__.<lambda>>

未报告任何错误。

这是lambda的一瞥,它使您能够像在数学中一样直接在一行中编写函数。

我们稍后会看到它。

让我们继续深入挖掘“任务”。

如上图所示,等号=适用于简单数据(1和'值')类型和简单表达式(n ** 2 + 2 * n + 1)。

试试这个:

In [15]: x = print('This is a x')
This is a x
In [16]: x
In [17]: x = input('Enter a x: ')
Enter a x: x

它适用于简单的语句,在python 7. Simple statements — Python 3.6.3 documentation

中有11种类型

复合语句怎么样,

In [18]: m = n**2 + 2*n + 1 if n > 0
SyntaxError: invalid syntax
#or
In [19]: m = n**2 + 2*n + 1, if n > 0
SyntaxError: invalid syntax

def启用它

In [23]: def m(n):
    ...:     if n > 0:
    ...:         return n**2 + 2*n + 1
    ...:
In [24]: m(2)
Out[24]: 9

Tada,分析它,'m'是名字,'n ** 2 + 2 * n + 1'是值。:是'='的变体。
找到它,如果只是为了理解,一切都从作业开始,一切都是作业。

现在返回lambda,我们有一个名为'm'的函数

尝试:

In [28]: m = m(3)
In [29]: m
Out[29]: 16

这里有两个名字'm',函数m已经有一个名字,重复。

它的格式如下:

In [27]: m = def m(n):
    ...:         if n > 0:
    ...:             return n**2 + 2*n + 1
    SyntaxError: invalid syntax

这不是一个聪明的策略,所以错误报告

我们必须删除其中一个,设置一个没有名字的函数。

m = lambda n:n**2 + 2*n + 1

它被称为'匿名功能'

总之,

  1. lambda在内联函数中,您可以像在数学中一样在一条直线上编写函数
  2. lambda是匿名的
  3. 希望,这有帮助。

答案 14 :(得分:2)

例如,在Javascript中,函数被视为与其他所有函数相同的混合类型(intstringfloatbool)。因此,您可以动态创建函数,将它们分配给事物,稍后再调用它们。它很有用,但不是你想过度使用的东西,或者你会混淆每个必须在你之后维护代码的人...

这是我正在玩的一些代码,看看这个兔子洞有多深:

var x = new Object;
x.thingy = new Array();
x.thingy[0] = function(){ return function(){ return function(){ alert('index 0 pressed'); }; }; }
x.thingy[1] = function(){ return function(){ return function(){ alert('index 1 pressed'); }; }; }
x.thingy[2] = function(){ return function(){ return function(){ alert('index 2 pressed'); }; }; }

for(var i=0 ;i<3; i++)
    x.thingy[i]()()();

答案 15 :(得分:2)

在CS的背景下,lambda函数是一个抽象的数学概念,它解决了数学表达式的符号评估问题。在该上下文中,lambda函数与lambda term相同。

但在编程语言中,它有所不同。它是一段声明的代码&#34;就位#34;它可以作为一流的公民传递#34;。这个概念似乎很有用,因此几乎所有流行的现代编程语言都有用(见lambda functions everwhere post)。

答案 16 :(得分:2)

这是一个没有名字的功能。对于例如在c#中你可以使用

numberCollection.GetMatchingItems<int>(number => number > 5);

返回大于5的数字。

number => number > 5

是这里的lambda部分。它表示一个函数,它接受一个参数(数字)并返回一个布尔值(数字> 5)。 GetMatchingItems方法在集合中的所有项目上使用此lambda,并返回匹配的项目。

答案 17 :(得分:1)

我也懂了。我在JS中尝试过这个:

var addAndMult = function(x) {
        return (function(y) {
            return (function(z) {
                return (x+y)*z; 
                });
            });
        };

它增加了2到4然后结果为6.然而我发现它有时难以阅读:(

我也为每个函数做了一个有趣的事情:

var forEach = function(arr) {
            return (function(x) {
            for (var i=0; arr[i]; i++) {
                 x(arr[i]);
             }
        });
    }

的forEach([1,2,3,4,5])(的console.log);

此方法将迭代数组并执行操作 - 在打印到控制台的情况下。现在我也明白了为什么labmdas是强大的。

答案 18 :(得分:1)

在计算机编程中,lambda是一段代码(语句,表达式或其中的一组),它从外部源获取一些参数。它不一定总是一个匿名函数 - 我们有很多方法来实现它们。

我们在数学家没有的表达式,陈述和函数之间有明确的分离。

编程中的“功能”一词也不同 - 我们有“功能是一系列要做的步骤”(来自拉丁语“perform”)。在数学中,它是变量之间的相关性。

功能语言试图尽可能与数学公式相似,它们的意思几乎相同。但在其他编程语言中,我们有不同的。

答案 19 :(得分:1)

  

Lambda FunctionSmall Anonymous Function是一个自包含的功能块,可以在代码中传递和使用。 Lambda在不同的编程语言中具有不同的名称-在 Python Kotlin 中为Lambda,在 Swift 中为Closure或{ {strong}和 Objective-C 中的{1}}。尽管lambda的含义在这些语言中非常相似,但有时还是有些许区别。

让我们看看Lambda(Closure)如何在Swift 4.2中使用sorted()方法-从正常函数到最短表达式:

Block

1。正常功能

let coffee: [String] = ["Cappuccino", "Espresso", "Latte", "Ristretto"]

2。闭包表达式

func backward(_ n1: String, _ n2: String) -> Bool {
    return n1 > n2
}
var reverseOrder = coffee.sorted(by: backward)


// RESULT: ["Ristretto", "Latte", "Espresso", "Cappuccino"]

3。内联闭包表达式

reverseOrder = coffee.sorted(by: { (n1: String, n2: String) -> Bool in
    return n1 > n2
})

4。从上下文推断类型

reverseOrder = coffee.sorted(by: { (n1: String, n2: String) -> Bool in return n1 > n2 } )

5。单表达式闭包的隐式返回

reverseOrder = coffee.sorted(by: { n1, n2 in return n1 > n2 } )

6。速记参数名称

reverseOrder = coffee.sorted(by: { n1, n2 in n1 > n2 } )

7。运算符方法

reverseOrder = coffee.sorted(by: { $0 > $1 } )

// $0 and $1 are closure’s first and second String arguments.

希望这会有所帮助。

答案 20 :(得分:1)

Lambda向所有人解释:

Lambda是一个匿名函数。这意味着lambda是Python中的一个对象,以前不需要引用。让我们在这里考虑一下这段代码:

def name_of_func():
    #command/instruction
    print('hello')

print(type(name_of_func))   #the name of the function is a reference
                            #the reference contains a function Objekt with command/instruction

为了证明我的主张,我打印出了name_of_func的类型,该类型返回了我们:

<class 'function'>

一个函数必须有一个接口,但是接口主体必须包含一些东西。这是什么意思?让我们更接近我们的函数,我们可能会注意到,在函数名称之外,我们还需要解释一些更多的细节以了解什么是函数。

将使用语法 “ def” 定义常规函数,然后输入名称并使用 “()结算接口。 “ ,并以语法 ”:“ 结束我们的定义。现在,我们使用指令/命令进入函数主体。

所以让我们在这里考虑一下这段代码:

def print_my_argument(x):
    print(x)


print_my_argument('Hello')

在这种情况下,我们运行名为“ print_my_argument”的函数,并通过该接口传递参数/参数。输出将是:

Hello

因此,既然我们知道什么是函数以及该函数的体系结构是如何工作的,我们就可以看看一个匿名函数。让我们在这里看一下这段代码:

def name_of_func():
    print('Hello')



lambda: print('Hello')

这些功能对象几乎相同,不同的是上层的常规功能具有名称,而另一个功能是匿名的。让我们仔细看看我们的匿名函数,以了解如何使用它。

所以让我们在这里考虑一下这段代码:

def delete_last_char(arg1=None):
    print(arg1[:-1])

string = 'Hello World'
delete_last_char(string)

f = lambda arg1=None: print(arg1[:-1])
f(string)

因此,我们在上面的代码中所做的是再次编写了g,一个常规函数和一个匿名函数。我们已将匿名函数分配给var,该函数与为其指定名称几乎相同。无论如何,输出将是:

Hello Worl
Hello Worl

为了充分证明lambda是一个函数对象,而不仅仅是mimik一个函数,我们在此处运行以下代码:

string = 'Hello World'
f = lambda arg1=string: print(arg1[:-1])
f()
print(type(f))

,输出将是:

Hello Worl
<class 'function'>

最后但并非最不重要的一点是,您应该知道python中的每个函数都需要返回某些内容。如果函数主体中未定义任何内容,则默认情况下将不返回None。在这里查看这段代码:

def delete_last_char(arg1):
    print(arg1[:-1])

string = 'Hello World'
x = delete_last_char(string)

f = lambda arg1=string: print(arg1[:-1])
x2 = f()

print(x)
print(x2)

输出将是:

Hello Worl
Hello Worl
None
None

答案 21 :(得分:0)

这个问题已经得到了充分的回答,我不想详细介绍。我想在生锈中编写数值计算时分享用法。

有一个lambda(匿名函数)

的例子
let f = |x: f32| -> f32 { x * x - 2.0 };
let df = |x: f32| -> f32 { 2.0 * x };

当我编写Newton-Raphson方法的模块时,它被用作一阶和二阶导数。 (如果你想知道什么是Newton-Raphson方法,请访问&#34; https://en.wikipedia.org/wiki/Newton%27s_method&#34;。

输出如下

println!("f={:.6}      df={:.6}", f(10.0), df(10.0))

f=98.000000       df=20.000000

答案 22 :(得分:0)

想象一下,您有一家提供送餐服务的餐厅,而您的订单需要在30分钟内完成。关键是客户通常不在乎您是否将食物加热并绑起来,而骑车还是赤脚骑自行车送他们的食物。因此,让我们使用匿名的和已定义的传输函数将此习语转换为Javascript。

下面我们定义了传递方式,即我们为函数定义了一个名称:

// ES5 
var food = function withBike(kebap, coke) {
return (kebap + coke); 
};

如果我们要使用arrow / lambda函数来完成此转移该怎么办:

// ES6    
const food = (kebap, coke) => { return kebap + coke };

您看到客户没有区别,也没有时间浪费在考虑如何发送食物上。只需发送即可。

顺便说一句,我不建议将可乐与可乐混在一起,这就是为什么高位代码会给您错误的原因。玩得开心。

答案 23 :(得分:-3)

lambda函数可以接受任意数量的参数,但是它们仅包含一个表达式。 ... Lambda函数可用于返回函数对象。 从语法上讲,lambda函数仅限于单个表达式。