【问题标题】:Special closure use-cases: Why use closures?特殊闭包用例:为什么要使用闭包?
【发布时间】:2010-10-18 23:37:07
【问题描述】:

更新

我正在设计一种实验性编程语言,问题是是否包含闭包或仅使用一流函数。为了决定这一点,我需要现实的用例/示例来展示闭包优于一流函数的好处。我知道你可以实现一切,你可以用两个没有它们之一来实现,但是有几个用例,例如代码更易于阅读的一流功能(例如更短或不分为多个类)。例如:

鲁比:

[1,5,7].map{|x| x*x }
[1,'test',3].select{|x| x.kind_of? Integer}.map{|x| x.to_s }
big_array.each{ |item| puts item }

如果没有一流的函数,这些示例会更加冗长,因为您将不得不使用 for 循环或类似的东西。

现在,哪些用例显示了闭包的用处?尽管我经常使用一流的函数,但我真的想不出好的闭包用例。你有什么好的闭包用例吗?

原帖

我不明白为什么闭包绑定到变量,而不仅仅是,例如:

鲁比:

x = 5
l = lambda { x }
l.call #=> 5
x = 100
l.call #=> 100

引用变量而不是仅仅在定义闭包时引用存储在变量中的值有什么用?就像在这个例子中一样:

鲁比:

x = 5
l = lambda { x }
l.call #=> 5
x = 100
l.call #=> 5, not 100

是否存在需要在定义闭包时引用 变量 而不仅仅是这些变量的 的良好用例?

【问题讨论】:

    标签: binding closures environment use-case


    【解决方案1】:

    闭包的全部意义在于关闭状态可以改变。可以说,关闭不(或不能)改变的东西是不好的形式,因为你隐藏了一个参数。

    以 C# 中的以下人为示例:

    var sb=new StringBuilder();
    int counter = 0;
    var Append=(int a, string s)=>
                sb.Append(a*a + counter++, SomethingElse(s, "some constant"));
    

    Append 关闭 StringBuilder 和计数器,这样我就可以通过我的代码对它进行调用,而不必每次都通过 SomethingElsesb.Append 仪式。如果闭包内部的状态不能改变,那么闭包对这种事情的用处就会降低。

    【讨论】:

    • 我想你误解了我(或者我误解了你的回答:P):看看我发布的第一个例子。我为在闭包外部定义的变量 x 分配了一个新值。尽管这个赋值发生在 闭包被定义之后,闭包使用 x 的 new 值(100 而不是 5)。为什么闭包不直接使用在定义闭包时分配给 x 的值?
    • @Ragmaanir 因为这就是闭包的全部意义所在!如果它使用变量在定义时的值,那么它与参数的含义相同。如果这是您想要的行为,请将值作为参数传递,而不是关闭变量。
    • @DDaviesBrackett:我不明白这与参数有何相同之处。 IMO 它就像一个常量,因为您在调用闭包时无法替换该值。虽然可以在每次调用时传递参数。不使用参数的原因是您必须在每次调用时传递它们。但是,在定义闭包之后,您需要闭包来识别对变量的重新分配的用例是什么?在您的示例中,这(仅)用于我认为的计数器变量。这种行为可能是针对 non-mutable 值(如 int)吗?
    • @Ragmaanir:DDaviesBrackett 是正确的——捕获(即关闭)变量是闭包的重点。听起来你真正想要的是partial function application,有时称为beta reduction,有时称为incorrectly called currying
    • @Daniel Pryden:我的第一条评论表述不正确,抱歉。我知道关闭变量是关闭的重点。这就是为什么我更新了我的问题:有两种类型的闭包:更容易实现的类型关闭变量而不跟踪对变量的重新分配,更难实现的闭包类型跟踪对使用的变量的重新分配在闭包中,甚至在 闭包被定义之后。后一个的目的是什么?在 prog 中包含这种类型的闭包是否值得?语言?
    【解决方案2】:

    据我了解(如果我错了,请有人帮助我)在 lambda 表达式中使用变量将变量(例如“Foo as Widget”)更改为 New Holder(Of Widget),定义如下所示, 并将所有对 Foo 的引用更改为对 Foo.Value 的引用。然后将封闭变量的持有者作为参数传递给委托工厂。

    班级持有人(T) 公共价值作为 T 子新(NewValue As T) 价值 = 新价值 结束子 结束类

    我不太确定额外的包装层多久真正有用。我认为更常见的是人们想要按值获取局部变量。在我自己的代码中,在升级到支持 lambda 的 VB 版本之前,我使用了委托工厂,例如

    Sub SetText(ByVal St As String) 文字=圣 结束子 ...其他地方 MyControl.BeginInvoke(NewInv(AddressOf SetText, someExpression))

    请注意,someExpression 将在执行 BeginInvoke 本身时进行评估,而不是在实际执行延迟操作时进行评估。另请注意,我的 NewInv() 系列函数支持 1-4 个参数的通用覆盖,并且如果需要一个 Action(Of T) 而不是 MethodInvoker,我有一个通用静态类和另一个函数系列。

    顺便说一句,这种方法的一个优点是:它适用于编辑/继续。

    【讨论】:

      猜你喜欢
      • 2018-12-01
      • 2010-09-20
      • 1970-01-01
      • 2018-08-25
      • 1970-01-01
      • 2017-10-05
      • 2022-09-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多