代码之家  ›  专栏  ›  技术社区  ›  Aggelos Biboudis

带闭包的无缺陷编码(在非纯虚拟环境中)

  •  2
  • Aggelos Biboudis  · 技术社区  · 15 年前

    最近几天,我在努力理解关闭。我非常喜欢C,所以我的主要测试平台是这种语言,所以我想了解它的闭包支持。在我学习和试验的过程中,我发现很多人在尝试写关于闭包的博客时,都是按照一个完全错误的方向来做的。他们设计了一种对闭包的错误使用,比如众所周知的语句,并且他们试图解释它。相反,我希望看到一种数学方法(一等公民、自由/约束变量、lambda等)。然而,这让我认为我想知道在不考虑闭包的情况下编码时可能会出现什么错误。

    此外,所有语言对闭包的数学构造都有相同的解释吗?

    我没有FP课程,也没有uni的高级编程语言。但我知道副作用在程序代码中的作用,以及它们在纯虚拟语言中不存在的作用。C中的闭包仅仅是一个技巧吗?什么(例如)F闭包比C闭包多?

    4 回复  |  直到 15 年前
        1
  •  5
  •   Tomas Petricek    15 年前

    首先,我认为应该怎么称呼有些困惑 关闭 应该叫什么 λ函数 . 我认为正确的方法是调用语言中的句法元素(例如 (a, b) => a + b 在C)lambda函数中。创建的值是 函数值 或A 代表 在C语言中。

    正如在.NET中实现的(两个F&C),该 代表 实际上是对某个类中某个方法的引用。原因是 代表 使用 lambda函数 语法可能需要保持某种状态:

    Func<int, int> CreateAdder(int num) {
      return arg => arg + num;
    }
    

    归还的人 代表 引用一些(未命名)对象,这些对象存储 num 价值以及 λ函数 . 那么,什么是 关闭 ?闭包是一个对象,它保持运行 函数值 (或) 德尔盖特 )。在这种情况下,它是未命名的对象,从 代表 并保持 号码 .

    你也提到过 自由的 跳跃 变量。如果你看 lambda函数 在上面的例子中,它使用两个变量。变量 arg 声明为 λ函数 . 这个叫 约束变量 (在 λ函数 ,因为它被声明为lambda函数的一部分。这个 号码 变量将被称为 自由变量 λ函数 ,因为它只被使用(但没有声明!)在 λ函数 . 它来自外部范围(在本例中是方法声明)。

    这个 关闭 需要捕获所有 自由的 中的变量 λ函数 .这意味着在主体内部使用但在其他地方声明的所有变量都将被捕获。但是,C编译器不只是复制当前值。它把它变成一个可变的字段,这样就可以从所有可能访问它的函数(这也是它变得棘手的地方)访问它(也会发生变化)。这是一篇很长的博客文章的主题,但是这里有一个简单的例子,你可以用它来做实验(使用 Tuple 从.NET 4.0中,如果您使用的是VS 2008,则可以获得C实现 here in Chapter03/FunctionalCSharp ):

    Tuple<Func<int>, Action<int>> CreateReaderAndWriter(int initial) {
       int state = initial;
       return Tuple.Create( (() => state),
                            (newState => { state = newState; }) );
    }
    

    当您调用这个方法时,结果会得到两个函数。第一个允许您读取当前状态,第二个允许您修改它。注意状态是共享的(因为它是相同的可变变量)!

    有一个 proposal by Don Syme from MSR 直接向.NET添加对闭包的支持。这有点学术性,但可能有助于澄清一些事情。

        2
  •  4
  •   kvb    15 年前

    闭包的概念在不同的语言中是相当一致的,尽管在命令式语言中,对于诸如 continue , break return 应该在一个闭包中处理(例如,将闭包添加到Java中的一些提案在这方面的行为与C语言相比不同)。吸引人们注意的主要微妙之处在于,在非纯语言中,闭包“封闭”变量绑定而不是值,这意味着变量作用域之类的东西非常重要(这也是for循环示例中出现意外困难的地方,因为循环变量的作用域并不总是人们所期望的)。

    f的行为与c的行为非常相似,但是有一些事情使得闭包在f中的工作更容易一些。首先,虽然f是不纯的,但是突变是不受鼓励的,所以很难编写一个闭包,它无意中关闭了一个变量,而这个变量后来被修改成一种违背期望的方式。特别是,F编译器不允许在闭包内使用普通可变绑定-编译器的错误消息建议您要么使绑定不可变,要么使用显式引用单元格(类型 'a ref 在f)中,如果您实际上打算关闭一个可能发生变异的绑定。这迫使用户仔细考虑要实现的目标。

        3
  •  1
  •   Nathan Shively-Sanders    15 年前

    在我看来,你想学习一般的函数式编程。这样做,您就不能避免学习使用闭包的“正确”方法,因为它们对函数式编程非常重要。

    不幸的是,我不知道一个好的函数式编程参考。搜索一点就会找到这篇介绍文章: Introduction to Functional Programming in C# .

    如果你不介意使用其他语言,你可以考虑 The Little Schemer . 它使用方案,但只使用书中实际需要的部分。它很容易理解,但是可以直接深入到函数编程的硬部分。

    至于你的另一个问题,我发现如果不改变变量,闭包在大多数语言中都是相同的——甚至Java的匿名内部类。(尽管,正如kvb所说,函数语言如f和haskell确实可以防止您在无意中错误地改变变量。)

        4
  •  0
  •   Lazarus    15 年前

    你读过马丁·福勒关于这个话题的论述吗?它似乎涵盖了您的担忧,并且来自一个相当权威的数字: http://martinfowler.com/bliki/Closure.html