使用do块与花括号{}


112

红宝石的新手,戴上新手手套。

以下两个摘要之间是否有任何区别(晦涩或实际)?

my_array = [:uno, :dos, :tres]
my_array.each { |item| 
    puts item
}

my_array = [:uno, :dos, :tres]
my_array.each do |item| 
    puts item
end

我意识到大括号语法可以让您将代码块放在一行上

my_array.each { |item| puts item }

但是除此之外,还有什么令人信服的理由使用一种语法而不是另一种?


5
好问题。我也对经验丰富的红宝石学家的偏爱感兴趣。
乔纳森·斯特林




1
您也可以编写一个do块衬里,尽管它仅在执行eval(“ my_array.each do | item |; puts item; end”)之类的操作时才真正有用,但是它可以在irb或pry中工作,而没有eval和引号首选时可能会遇到这种情况。不过不要问我。这是另一个要研究的话题。
道格拉斯·艾伦

Answers:


100

Ruby Cookbook说方括号语法的优先顺序比do..end

请记住,方括号语法的优先级高于do..end语法。考虑以下两个代码段:

1.upto 3 do |x|
  puts x
end

1.upto 3 { |x| puts x }
# SyntaxError: compile error

第二个示例仅在使用括号时有效, 1.upto(3) { |x| puts x }


7
知道了 因此,由于优先顺序,使用do时,会将块作为附加参数传递,但是当使用方括号时,将块作为方法调用结果的第一个参数传递给左边。
艾伦·斯托姆

2
我通常更喜欢简短的答案,但bkdir的答案要清晰得多。
yakout

70

这是一个有点老问题,但我想尝试解释一下详细了解{}do .. end

就像之前所说的

方括号语法的优先级比..end高

但是这一点有何不同:

method1 method2 do
  puts "hi"
end

在这种情况下,method1将与的块一起调用,do..end而method2将作为参数传递给method1!相当于method1(method2){ puts "hi" }

但是如果你说

method1 method2{
  puts "hi"
}

然后将使用该块调用method2,然后将返回值作为参数传递给method1。相当于method1(method2 do puts "hi" end)

def method1(var)
    puts "inside method1"
    puts "method1 arg = #{var}"
    if block_given?
        puts "Block passed to method1"
        yield "method1 block is running"
    else
        puts "No block passed to method1"
    end
end

def method2
    puts"inside method2"
    if block_given?
        puts "Block passed to method2"
        return yield("method2 block is running")
    else
        puts "no block passed to method2"
        return "method2 returned without block"
    end
end

#### test ####

method1 method2 do 
    |x| puts x
end

method1 method2{ 
    |x| puts x
}

####输出####

#inside method2
#no block passed to method2
#inside method1
#method1 arg = method2 returned without block
#Block passed to method1
#method1 block is running

#inside method2
#Block passed to method2
#method2 block is running
#inside method1
#method1 arg = 
#No block passed to method1

39

通常,约定是{}在执行小操作(例如方法调用或比较等)时使用的,因此这很有意义:

some_collection.each { |element| puts element }

但是,如果您的逻辑稍微复杂一些,可以使用多行代码,则可以使用do .. end

1.upto(10) do |x|
  add_some_num = x + rand(10)
  puts '*' * add_some_num
end

基本上,归结为,如果您的块逻辑走到多行并且不能放在同一行上,则使用do .. end;如果您的块逻辑是简单的,则只需简单/单行代码,则使用{}


6
我同意在多行代码块中使用do / end,但是如果将其他方法链接到代码块的末尾,我将使用花括号。从风格上讲,我喜欢{...}。method()。method()胜过do ... end.method()。method,但那可能就是我。
Tin Man

1
同意,尽管我更喜欢将带有block的方法的结果分配给有意义的变量,然后在其上调用另一个方法,因为那样result_with_some_condition = method{|c| c.do_something || whateever}; result_with_some_condition.another_method会使它更容易理解。但总的来说,我会避免火车残骸。
–'nas

我想知道为什么这种风格如此受欢迎。除了“我们总是这样做”以外,还有其他原因吗?
iGEL

9

还有是选择两种常见的方式do end{ }使用Ruby块:

第一种也是非常常见的样式已由Ruby on Rails普及,它基于单行还是多行的简单规则:

  • { }对单行块使用大括号
  • 使用do end多行块

这是有道理的,因为do / end在单行代码中读取效果很差,但是对于多行代码块,将结束}符挂在自己的行上与end红宝石中使用的其他所有内容(例如模块,类和方法定义(def等)不一致。)和控制结构(ifwhilecase等)

第二种较少见的样式是语义的,即已故的红宝石学家吉姆·魏里希(Jim Weirich)提出的“ 韦里奇括号 ”:

  • 使用do end的程序块
  • { }对功能块使用花括号

这意味着在评估该块的返回值时,它应该是可链接的,并且花{}括号对于方法链接更有意义。

另一方面,当评估该块的副作用时,则返回值无关紧要,并且该块只是“在做”某件事,因此链接没有意义。

语法上的这种区别传达了有关块评估以及是否应关心其返回值的视觉含义。

例如,此处将块的返回值应用于每个项目:

items.map { |i| i.upcase }

但是,这里不使用块的返回值。它的工作程序上,和与它的副作用:

items.each do |item|
  puts item
end

语义样式的另一个好处是,您无需更改大括号即可执行/结束操作,因为仅在行中添加了一行。

观察到,巧合的功能块通常是单线的,而过程块(例如config)是多行的。因此,遵循Weirich风格最终看起来与Rails风格几乎相同。


1

我使用了Weirich样式多年,但后来逐渐放弃使用它,而是始终使用花括号。我不记得曾经使用过来自块样式的信息,而且定义有点模糊。例如:

date = Timecop.freeze(1.year.ago) { format_date(Time.now) }
customer = Timecop.freeze(1.year.ago) { create(:customer) }

这些是程序性的还是功能性的?

在我看来,行数是没有用的。我知道,是否有1行或更多行,为什么仅由于添加或删除行而更改样式?

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.