方法喜欢maps并且collect只接受块.如果块不是对象而不能保存到变量,那么为什么这些方法需要它们作为参数?
multiples_of_2 = Proc.new do |x|
x % 2 == 0
end
sq = Proc.new { |x| x ** 2 }
(1..50).to_a.select(&multiples_of_2) # => works properly
(1..50).to_a.select(multiples_of_2) # => `wrong number of arguments(1 for 0)`
[4, 5, 6].map!(sq) # => `wrong number of arguments(1 for 0)`
[10, 12, 14].collect!(sq) # => `wrong number of arguments(1 for 0)`
Run Code Online (Sandbox Code Playgroud)
在设计Ruby之前,Matz分析了Common Lisp,Smalltalk和ML等语言中高阶程序的用法,他注意到绝大多数高阶程序只采用一个一等程序作为参数而它们没有存储它或传递它,但只是调用它.在那些采用多个一流程序作为参数的程序中,相当多的是控制结构,例如条件和循环,他不想作为方法.
因此,他的分析结果是:有一个构造允许您将一个代码块传递给一个未被存储或进一步处理的方法,只是执行,足以覆盖80%的所有情况.从我个人的经验来看,我当然可以证明这一点.我唯一需要多个块的时候,就是当我想要显示if/ then/ else作为一个方法的实现时.
因此,他设计了一种语法和语义轻量级方法,将单个代码块传递给方法.这种限制允许一些便利.例如,如果您知道不能有多个块,那么您不需要为其命名,因为您始终知道您正在谈论哪个块(只有一个块).这使得类似的语法轻量级yield关键字的工作:你可以说yield无需指定地方产生对,因为只有一个街区.
但是,对于那些需要能够传递多个可执行代码块,或者进一步存储和处理它们的情况,我们有Procs,我们有一种简单的方法在Procs和块之间进行转换:在参数列表中,的&印记是指"打包块划分成Proc并将其绑定到该名称",并在参数列表中,&一元前缀操作者的意思是"解包Proc成块"(如果它不是一个Proc,通过调用其转换为一个第一to_proc).
既然我们已经有了stabby lambda文字,那么在语言中使用块而不使用它们之间的区别并不像以前那么大:
enum.map(-> e { e ** 2 })
# vs.
enum.map {|e| e ** 2 }
Run Code Online (Sandbox Code Playgroud)
但在Ruby 1.9之前,差异是:
enum.map(Proc.new {|e| e ** 2 })
# vs.
enum.map {|e| e ** 2 }
Run Code Online (Sandbox Code Playgroud)
嗯,实际上我们在这里作弊!我们首先要通过一个街区Proc.new!但是我们只是说,如果我们没有块怎么办?那显然也行不通.你宁愿做这样的事情:
def (sq = Proc.new).call(e) e ** 2 end; enum.map(sq)
# vs.
enum.map {|e| e ** 2 }
Run Code Online (Sandbox Code Playgroud)
与在语言中使用块相比,这是一些重要的语法开销!
所以,这就是为什么我们有块,以及为什么类似于map只关心一个代码块并且不需要实际存储它的方法的原因.