当前位置:  开发笔记 > 编程语言 > 正文

函数指针,闭包和Lambda

如何解决《函数指针,闭包和Lambda》经验,为你挑选了4个好方法。

我刚才正在学习函数指针,当我正在阅读关于这个主题的K&R章节时,第一件让我感到惊讶的是,"嘿,这有点像一个闭包." 我知道这种假设在某种程度上是根本错误的,在网上搜索后我没有找到任何对这种比较的分析.

那么为什么C风格的函数指针与闭包或lambdas根本不同呢?据我所知,它与函数指针仍然指向已定义(命名)函数的事实有关,这与匿名定义函数的做法相反.

为什么将函数传递给在第二种情况下看起来更强大的函数,它是未命名的,而不是第一种传递的正常日常函数?

请告诉我如何以及为什么我错误地比较这两者.

谢谢.



1> Mark Bracket..:

lambda(或闭包)封装了函数指针和变量.这就是为什么在C#中你可以做到:

int lessThan = 100;
Func lessThanTest = delegate(int i) {
   return i < lessThan;
};

我在那里使用了一个匿名委托作为闭包(它的语法更清晰,更接近C而不是lambda等价物),它将lessThan(一个堆栈变量)捕获到闭包中.在计算闭包时,将继续引用lessThan(其堆栈框架可能已被销毁).如果我改变lessThan,那么我改变比较:

int lessThan = 100;
Func lessThanTest = delegate(int i) {
   return i < lessThan;
};

lessThanTest(99); // returns true
lessThan = 10;
lessThanTest(99); // returns false

在C中,这将是非法的:

BOOL (*lessThanTest)(int);
int lessThan = 100;

lessThanTest = &LessThan;

BOOL LessThan(int i) {
   return i < lessThan; // compile error - lessThan is not in scope
}

虽然我可以定义一个带有2个参数的函数指针:

int lessThan = 100;
BOOL (*lessThanTest)(int, int);

lessThanTest = &LessThan;
lessThanTest(99, lessThan); // returns true
lessThan = 10;
lessThanTest(100, lessThan); // returns false

BOOL LessThan(int i, int lessThan) {
   return i < lessThan;
}

但是,现在我在评估它时必须传递2个参数.如果我希望将此函数指针传递给另一个函数,其中lessThan不在范围内,我将不得不通过将其传递给链中的每个函数或通过将其提升为全局来手动保持它.

虽然支持闭包的大多数主流语言都使用匿名函数,但并不要求这样做.你可以拥有没有匿名函数的闭包,以及没有闭包的匿名函数.

总结:闭包是函数指针+捕获变量的组合.



2> Norman Ramse..:

作为一个为有和没有"真正"封闭的语言编写程序的人,我恭敬地不同意上面的一些答案.Lisp,Scheme,ML或Haskell闭包不会动态创建新函数.相反,它重用现有函数,但使用新的自由变量.自由变量的集合通常被称为环境,至少是编程语言理论家.

闭包只是一个包含函数和环境的聚合.在新泽西标准ML编译器中,我们将一个表示为记录; 一个字段包含指向代码的指针,其他字段包含自由变量的值.编译器通过分配包含指向同一代码但具有不同自由变量值的指针的新记录来动态创建新的闭包(非函数).

你可以在C中模拟所有这些,但这是一个痛苦的屁股.两种技术很受欢迎:

    传递指向函数(代码)的指针和指向自由变量的单独指针,以便将闭包分割为两个C变量.

    将指针传递给struct,其中struct包含自由变量的值以及指向代码的指针.

当您尝试在C中模拟某种多态性并且您不想揭示环境的类型时,技术#1是理想的- 您使用void*指针来表示环境.例如,看看Dave Hanson的C接口和实现.技术#2更类似于函数式语言的本机代码编译器中发生的事情,也类似于另一种熟悉的技术......具有虚拟成员函数的C++对象.实现几乎相同.

这一观察导致了亨利贝克的睿智:

Algol/Fortran世界的人们多年来一直抱怨他们不了解在未来的有效编程中可能使用的函数闭包.然后发生了"面向对象编程"革命,现在每个人都使用函数闭包编程,除了他们仍然拒绝调用它们.



3> Herms..:

在C中你不能定义内联函数,所以你不能真正创建一个闭包.您所做的只是传递对某些预定义方法的引用.在支持匿名方法/闭包的语言中,方法的定义更加灵活.

简单来说,函数指针没有与它们相关联的范围(除非计算全局范围),而闭包包括定义它们的方法的范围.使用lambdas,您可以编写一个编写方法的方法.闭包允许您将"某些参数绑定到函数并获得较低的arity函数".(取自托马斯的评论).你不能用C做那个.

编辑:添加一个例子(我将使用Actionscript-ish语法导致这就是我现在的想法):

假设您有一些方法将另一个方法作为其参数,但是在调用时不提供将任何参数传递给该方法的方法?比方说,在运行你传递它的方法之前会导致延迟的一些方法(愚蠢的例子,但我想保持简单).

function runLater(f:Function):Void {
  sleep(100);
  f();
}

现在假设你想用runLater()来延迟一些对象的处理:

function objectProcessor(o:Object):Void {
  /* Do something cool with the object! */
}

function process(o:Object):Void {
  runLater(function() { objectProcessor(o); });
}

您传递给process()的函数不再是一些静态定义的函数.它是动态生成的,并且能够包含对定义方法时范围内的变量的引用.因此,它可以访问'o'和'objectProcessor',即使它们不在全局范围内.

我希望这是有道理的.



4> Jon Skeet..:

闭包=逻辑+环境.

例如,考虑这个C#3方法:

public Person FindPerson(IEnumerable people, string name)
{
    return people.Where(person => person.Name == name);
}

lambda表达式不仅封装了逻辑("比较名称"),还封装了环境,包括参数(即局部变量)"name".

有关这方面的更多信息,请查看我关于闭包的文章,其中介绍了C#1,2和3,展示了闭包如何使事情变得更容易.

推荐阅读
k78283381
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有