Code block passed to each works with brackets but

2019-01-01 13:32发布

问题:

I recently started learning ruby, and I understood that you coud use code blocks with both of these syntaxes. But I just found a case which I dont understand:

#my_hash is a hash in which the keys are strings and the values arrays, but dont think about the specifics fo the code

#if I run my code like this, it works perfectly

my_hash.each do |art|
  puts mystring.gsub(art[0]).each {
    art[1][rand(art[1].length) -1]
  }
end

#but if I use this, it prints \"Enumerator\"

my_hash.each do |art|
  puts mystring.gsub(art[0]).each do
    art[1][rand(art[1].length) -1]
  end
end

Is it because you cant nest do-end pairs? I am using 1.9

回答1:

puts mystring.gsub(art[0]).each do
  art[1][rand(art[1].length) -1]
end

Here you called puts without parens, the do ... end refers to the puts method, that does nothing with a block and prints mystring.gsub(art[0]).each (with is a Enumerator).

The { ... } is called with the nearest method. Becomes ugly, but you can do it with do ... end:

puts(mystring.gsub(art[0]).each do
  art[1][rand(art[1].length) -1]
end)

Or, better, put the result in a variable and print the variable:

var = mystring.gsub(art[0]).each do
  art[1][rand(art[1].length) -1]
end
puts var

Anyway, the each don\'t changes the object, it just iterate and returns the object itself. You may be wanting the map method, test it.



回答2:

Expanding on Scott\'s reply, and quoting Jim Weirich:

The difference is where in the operator precedence table they fall. { } binds tighter than do/end. For example:

f g { }

is parsed as f(g { }), where the braces belong to the method g. On the other hand,

f g do end

is parsed as f(g) do end, where the braces belong to the method f. It only matters when you omit parenthesis and create ambiguities.



回答3:

Nesting do/end pairs is perfectly legal in Ruby, but you are coming up against a subtle precedence issue between { } and do/end.

You can read a little more about that here.



标签: ruby