简体   繁体   中英

Why does each_cons yield arrays instead of multiple values?

I just wanted to apply a binary operation to consecutive elements in an array, eg:

[1, 2, 3, 4].each_cons(2).map { |a, b| a.quo(b) }
#=> [(1/2), (2/3), (3/4)]

This is a contrived example, the operation doesn't really matter.

I was surprised, that I couldn't just write:

[1, 2, 3, 4].each_cons(2).map(&:quo)
#=> NoMethodError: undefined method `quo' for [1, 2]:Array

This is because each_cons doesn't yield multiple values, but an array containing the values.

It works like this:

def each_cons_arrays
  return enum_for(__method__) unless block_given?
  yield [1, 2]
  yield [2, 3]
  yield [3, 4]
end

each_cons_arrays.map(&:quo)
#=> NoMethodError: undefined method `quo' for [1, 2]:Array

And I was hoping for:

def each_cons_values
  return enum_for(__method__) unless block_given?
  yield 1, 2
  yield 2, 3
  yield 3, 4
end

each_cons_values.map(&:quo)
#=> [(1/2), (2/3), (3/4)]

What's the rationale behind this? Why could it be preferable to always have an array?

And by the way, with_index on the other hand does yield multiple values:

[1, 1, 1].each.with_index(2).map(&:quo)
#=> [(1/2), (1/3), (1/4)]

From my experience, it helps to think of multiple values in ruby as an array.

It think of it at as

[1,2,3].each_cons(2) do |iter|
  a,b = iter
  do_stuff(a,b)
end

If you want to do it like that, I'd add the quo method to a custom class, and do

class Foobar
  def initialize(a,b)
    @a = a
    @b = b
  end

  def quo
    do_stuff
  end
end

[1,2,3]
  .each_cons(2)
  .map { |a,b| Foobar.new(a,b) }
  .map(:quo)

Would that work for your usecase?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM