据我所知,Ruby中基本上有三种不同的闭包;方法,过程和lambda。我知道它们之间存在差异,但是我们能不能只使用一种适应所有可能用例的类型?
方法已经可以通过调用self.method(method_name)
来传递,比如procs和lambdas,而我在procs和lambdas之间意识到的唯一显着差异是lambdas检查arity和procs做了疯狂的事情当你试图使用return
。那么我们不能将它们全部合并为一个并完成它吗?
答案 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
(就像块一样)并且它们完全像块一样绑定参数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试图使用功能和面向对象范式的表达式使开发人员尽可能高效,这使得不同类型的闭包“语法糖”。