How does the block form of Array#new work given “A

2019-03-06 04:00发布

问题:

I am having trouble understanding the part inside the curly braces.

Array.new(10) { |e| e = e * 2 }
# => [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]   

I get that a new array with ten values is created, but what is the second half doing?

回答1:

Let's go over this in details:

nums = Array.new(10)

This creates a new array with 10 elements. For each array element it passes control to the block specified by:

{ |e| e = e * 2 }

The |e| represents the element's index. The index is the position in the array. This starts at 0 and ends at 9 since the array has 10 elements. The second part multiplies the index by 2 and returns the value. This is because the e * 2, being the last statement in the block, is returned. The value returned is then applied to that element's value. So we end up with the following array:

[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

EDIT

As mentioned by pjs and to avoid problems down the road, a simpler way to write the same code would be:

Array.new(10) { |e| e * 2 }


回答2:

What Your Code Does

nums = Array.new(10) { |e| e = e * 2 }

Break it down step by step in IRB or Pry to see what your code is really doing. For example:

  1. Array.new(10) creates an array with 10 elements.
  2. Array.new(10) { |e| puts e } prints the index of each element, which will be 0..9.
  3. The construct { |e| e = e * 2 }, or more idiomatically { |e| e * 2 }, is a Ruby block that multiplies the index of each element passed to the block variable e by 2.
  4. nums = Array.new(10) { |e| e = e * 2 } takes the result of the block and stores it in the variable nums.

Note that this stored array is created via the block form of Array#new, which says that when using the block form of the constructor:

[A]n array of the given size is created. Each element in this array is created by passing the element’s index to the given block and storing the return value.

The example in the original post is certainly valid Ruby code, but not particularly clear. The same result can be had with more explicit code, such as the example below.

One of Many Clearer Alternatives

There's always more than one way to do things, especially in Ruby. If you want to be more explicit about what your code is doing, use a less "magical" construct. For example, your code is roughly equivalent to:

num_array = 0.upto(9).map { |int| int * 2 }
#=> [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]