ruby为什么需要这么多不同类型的闭合?

时间:2013-08-09 12:16:32

标签: ruby

据我所知,Ruby中基本上有三种不同的闭包;方法,过程和lambda。我知道它们之间存在差异,但是我们能不能只使用一种适应所有可能用例的类型?

方法已经可以通过调用self.method(method_name)来传递,比如procs和lambdas,而我在procs和lambdas之间意识到的唯一显着差异是lambdas检查arity和procs做了疯狂的事情当你试图使用return。那么我们不能将它们全部合并为一个并完成它吗?

2 个答案:

答案 0 :(得分:14)

  

据我所知,Ruby中基本上有三种不同的闭包;方法,触发器和lambdas。

不,有两个:方法不是闭包,只有proc和lambdas。 (或者至少可以,大多数都不是。)

有两种方法可以打包一段可执行代码,以便在Ruby中重用:方法和块。严格来说,块不是必需的,你可以用方法来实现。但块意味着极轻,概念,语义和语法。对于方法来说,情况并非如此。

因为它们意味着重量轻且易于使用,所以块在某些方面与方法不同,例如,参数如何绑定到参数。块参数的绑定更像是赋值的左侧而不是方法参数。

示例:

将单个数组传递给多个参数:

def foo(a, b) end
foo([1, 2, 3]) # ArgumentError: wrong number of arguments (1 for 2)

a, b = [1, 2, 3]
# a == 1; b == 2

[[1, 2, 3]].each {|a, b| puts "a == #{a}; b == #{b}" }
# a == 1; b ==2

传递的参数少于参数:

def foo(a, b, c) end
foo(1, 2) # ArgumentError

a, b, c = 1, 2
# a == 1; b == 2; c == nil

[[1, 2]].each {|a, b, c| puts "a == #{a}; b == #{b}; c == #{c}" }
# a == 1; b == 2; c == 

传递的参数多于参数:

def foo(a, b) end
foo(1, 2, 3) # ArgumentError: wrong number of arguments (3 for 2)

a, b = 1, 2, 3
# a == 1; b == 2

[[1, 2, 3]].each {|a, b| puts "a == #{a}; b == #{b}" }
# a == 1; b == 2

[顺便说一下:上面没有任何一个是闭包。]

例如,这允许Enumerable协议始终为块生成单个元素以使用Hash es:您只需使单个元素Array [key, value]并且依赖于块的隐式数组解构:

{one: 1, two: 2}.each {|k, v| puts "#{key} is assigned to #{value}" }

比你原本写的更容易理解:

{one: 1, two: 2}.each {|el| puts "#{el.first} is assigned to #{el.last}" }

块和方法之间的另一个区别是方法使用return关键字返回值,而块使用next关键字。

如果你同意在语言中同时使用方法和块是有意义的,那么只接受proc和lambdas存在的一小步,因为它们分别表现为块和方法:

  • 从封闭方法中触发return(就像块一样)并且它们完全像块一样绑定参数
  • lambdas return来自他们(就像方法一样),他们绑定参数就像方法一样。

IOW:proc / lambda二分法只是反映了块/方法的二分法。

请注意,实际上还有很多案例需要考虑。例如,self是什么意思?是不是意味着

  • 无论self在编写块的时间点
  • 无论self在块运行点处是什么
  • 块本身

那么return呢?是不是意味着

  • 中写入块的方法返回
  • 中运行块的方法返回
  • 从街区返回?

这个已经为你提供了九种的可能性,甚至没有考虑了Ruby特有的参数绑定特性。

现在,出于封装的原因,上面的#2非常糟糕,所以这会减少我们的选择。

一如既往,这是语言设计师的品味问题。 Ruby中还有其他类似的冗余:为什么需要实例变量和局部变量?如果词法范围是对象,那么局部变量只是词法范围的实例变量,您不需要局部变量。为什么你需要实例变量和方法?其中之一就足够了:getter / setter方法对可以替换实例变量(请参阅Newspeak以获取此类语言的示例),分配给实例变量的第一类过程可以替换方法(请参阅Self,Python,JavaScript)。为什么你需要课程和模块?如果你允许类混合,那么你可以摆脱模块并使用类作为类和mixins。为什么你需要mixins呢?如果一切都是方法调用,那么类无论如何都会自动成为mixins(再次,请参阅Newspeak的示例)。当然,如果您允许在对象之间直接继承,则根本不需要类(请参阅Self,Io,Ioke,Seph,JavaScript)

答案 1 :(得分:3)

一些非常好的解释http://www.robertsosinski.com/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/但我猜你想要更深刻的哲学解释......

我相信答案“但我们不能只是让一种类型适应所有可能的用例吗?”,就是你只能使用一种方法就可以逃脱。

它们存在的原因是ruby试图使用功能和面向对象范式的表达式使开发人员尽可能高效,这使得不同类型的闭包“语法糖”。