使用do block vs braces {}

时间:2010-01-23 06:31:36

标签: ruby coding-style

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

以下两个片段之间是否有任何差异(模糊或实用)?

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 个答案:

答案 0 :(得分:97)

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 }

答案 1 :(得分:69)

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

像之前说的那样

  

括号语法的优先顺序高于do..end

但这个如何产生影响:

method1 method2 do
  puts "hi"
end

在这种情况下,将使用do..end块调用method1,并将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
}

#### output ####

#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

答案 2 :(得分: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并且如果您的块逻辑很简单并且只是简单/单行代码那么使用{}

答案 3 :(得分:4)

在Ruby中为块选择do end{ }有两种常见的样式:

Ruby on Rails推广了第一种非常常见的风格,它基于单行与多行的简单规则:

  • 使用大括号{ }作为单行块
  • do end用于多行块

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

第二种,不那么常见的风格被称为语义,或者是“Weirich Braces”,由已故的伟大的红宝石家吉姆·威里奇提出:

  • 使用do end进行程序块
  • 使用大括号{ }作为功能块

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

另一方面,当对块的副作用进行评估时,返回值无关紧要,并且块只是“做”某事,所以它不会感觉被束缚。

语法中的这种区别传达了关于块的评估的视觉含义,以及你是否应该关心它的返回值。

例如,这里块的返回值应用于每个项目:

items.map { |i| i.upcase }

但是,这里没有使用块的返回值。它在程序上运行,用它做副作用:

items.each do |item|
  puts item
end

语义风格的另一个好处是,您不需要更改大括号来执行/结束只是因为已将一行添加到块中。

作为观察,巧合地,功能块通常是单行的,并且过程块(例如,配置)是多行的。因此,遵循Weirich风格最终看起来与Rails风格几乎相同。

答案 4 :(得分:1)

我使用了Weirich风格多年,但后来从这种风格转移到始终使用牙套。我不记得曾经使用过来自块样式的信息,而且定义有点模糊。例如:

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

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

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