为什么有些方法只接受块作为Ruby中的参数?

时间:2015-06-02 11:53:46

标签: ruby arguments

mapscollect等方法只接受块。如果块不是对象而且不能保存到变量中,那么为什么这些方法需要它们作为参数呢?

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)`

1 个答案:

答案 0 :(得分:6)

Matz在设计Ruby之前分析了Common Lisp,Smalltalk和ML等语言中高阶程序的用法,他注意到绝大多数高阶程序首先完全一个 -class过程作为参数,它们不存储或传递它,只是调用它。而那些采用多个一等程序作为参数的程序,有很多是控制结构,例如条件和循环,他不想作为方法。

因此,他的分析结果是:拥有一个允许您将一个代码块完全传递给未被存储或进一步处理的方法的构造,只需执行,就足够了涵盖所有案例的80%。从我个人的经验来看,我当然可以证明这一点。我曾经需要多个块的唯一一次,就是当我想要显示if / then / else作为方法的实现时。

因此,他设计了一种语法和语义轻量级方法,将单个代码块传递给方法。这种限制允许一些便利。例如,如果您知道不能有多个块,那么您不需要为其命名,因为您始终知道您正在谈论哪个块(只有一个块)。这允许像语法上轻量级的yield关键字一样工作:你可以说yield而不必指定 where 来产生,因为那里只有一个街区。

但是,对于那些需要能够传递多个可执行代码块,或者进一步存储和处理它们的情况,我们有Proc个,我们有一个简单的方法来转换{ {1}} s和blocks:在参数列表中,Proc sigil表示“将块打包成&并将其绑定到此名称”并在参数列表中{{1}一元前缀运算符意味着“将Proc解包为一个块”(如果它不是&,请先通过调用Proc将其转换为一个。

既然我们已经有了stabby lambda文字,那么在语言中使用块而不使用它们之间的区别并不像以前那么大:

Proc

但在Ruby 1.9之前,差异是:

to_proc
嗯,好吧,实际上我们在这里作弊!我们首先将一个块传递给enum.map(-> e { e ** 2 }) # vs. enum.map {|e| e ** 2 } !但我们只是说,如果我们没有有阻塞怎么办?那显然也行不通。你宁愿做这样的事情:

enum.map(Proc.new {|e| e ** 2 })
# vs.
enum.map {|e| e ** 2 }

与在语言中使用块相比,这是一些重要的语法开销!

所以,这就是为什么我们有块,以及为什么类似Proc.new的方法,只关心一个代码块并且不需要实际存储在任何地方,为什么需要一个块。