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

Doy*_*dle 13 ruby

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

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

Jör*_*tag 14

据我所知,Ruby中基本上有三种不同的闭包; 方法,过程和lambda.

不,有两个:方法不是闭包,只有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
Run Code Online (Sandbox Code Playgroud)

传递的参数少于参数:

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 == 
Run Code Online (Sandbox Code Playgroud)

传递的参数多于参数:

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
Run Code Online (Sandbox Code Playgroud)

[顺便说一句:上面没有一个块是封闭的.]

这允许,例如,Enumerable它总是产生一个协议单一到块元件一起工作HashES:你只是使单个元件的Array[key, value]并依赖于该块的隐式阵列解构:

{one: 1, two: 2}.each {|k, v| puts "#{key} is assigned to #{value}" }
Run Code Online (Sandbox Code Playgroud)

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

{one: 1, two: 2}.each {|el| puts "#{el.first} is assigned to #{el.last}" }
Run Code Online (Sandbox Code Playgroud)

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

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

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

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)