3

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

agente_secreto
  • 7,959
  • 16
  • 57
  • 83
  • Not an answer, but I noticed you can simplify your hash iterator by using the `|k,v|` form. – Mark Thomas Jul 16 '11 at 17:44
  • This is a duplicate of [Ruby Block Syntax Error](http://StackOverflow.Com/q/6854283/), [Block definition - difference between braces and `do`-`end` ?](http://StackOverflow.Com/q/6179442/), [Ruby multiline block without `do` `end`](http://StackOverflow.Com/q/3680097/), [Using `do` block vs brackets `{}`](http://StackOverflow.Com/q/2122380/), [What is the difference or value of these block coding styles in Ruby?](http://StackOverflow.Com/q/533008/) and [Ruby block and unparenthesized arguments](http://StackOverflow.Com/q/420147/). – Jörg W Mittag Jul 28 '11 at 08:16

3 Answers3

9
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.

Guilherme Bernal
  • 8,183
  • 25
  • 43
  • Thank you, very helpful. In my case I am able to modify the object to .each, but I guess that is because I am using it with gsub, right? – agente_secreto Jul 16 '11 at 18:40
3

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.

Community
  • 1
  • 1
Denis de Bernardy
  • 75,850
  • 13
  • 131
  • 154
2

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.

Scott
  • 17,127
  • 5
  • 53
  • 64