Hi -talk,

I'm having trouble dealing with a block that yields one parameter but
declares two.  I've pasted complete test code below.  Basically, I've
defined "sum" on Enumerable that sums the result of yielding each value. 
This is for demonstration of the problem only.

When I define it like this

  def sum
    result = 0
    self.each { |elt| result += yield(elt) }
    result
  end

{1=>2}.sum { |k,v| v }   works but gives a warning.
[[1,2]].sum { |a,b| b }  is fine.

When I define it like this

  def sum
    result = 0
    self.each { |*elt| result += yield(*elt) }
    result
  end

{1=>2}.sum { |k,v| v }   is fine.
[[1,2]].sum { |a,b| b }  gives an error.


Below are the two definitions and a complete test case.  In the test case,
the range examples have no problems so are uncommented.  Comments are
given in the other cases.

I would really like to avoid the warning associated with the hash in
test_sum1h.

Thanks for any advice,
Gavin

--------------------------------------------------------------------------


module Enumerable
  #
  # Yield each element and return the (strictly numerical) sum of the
  # results.  sum1 and sum2 are different implementations.
  #

  def sum1
    result = 0
    self.each do |elt|
      result += yield(elt)
    end
    result
  end

  def sum2
    result = 0
    self.each do |*elt|
      result += yield(*elt)
    end
    result
  end
end


require 'test/unit'

#
# Test case: set up 3 enumerable objects, and try sum1 and sum2 on
# each of them.
#
class TC_Sum < Test::Unit::TestCase
  def setup
    @r = (1..10)                         # We'll sum the squares (385)
    @a = [ [1,2], [3,4], [5,6] ]         # We'll sum the products (44)
    @h = { :a => 1, :b => 2, :c => 3 }   # We'll sum the values (6)
  end

  # ======== First implementation

  def test_sum1r
    puts "test_sum1r"
    sum = @r.sum1 { |x| x ** 2 }
    assert_equal(385, sum)
  end

  def test_sum1a
    puts "test_sum1a"
    sum = @a.sum1 { |x,y| x * y } # Passes.
    assert_equal(44, sum)
  end

  def test_sum1h
    puts "test_sum1h"
    sum = @h.sum1 { |k,v| v }     # Passes, with warning:
    assert_equal(6, sum)          #   multiple values for a block
  end                             #   parameter (2 for 1)

  # ======== Second implementation

  def test_sum2r
    puts "test_sum2r"
    sum = @r.sum2 { |x| x ** 2 }
    assert_equal(385, sum)
  end

  def test_sum2a
    puts "test_sum2a"
    sum = @a.sum2 { |x,y| x * y } # Error: no implicit conversion
    assert_equal(44, sum)         #        from nil to integer
  end

  def test_sum2h
    puts "test_sum2h"
    sum = @h.sum2 { |k,v| v }     # Passes.
    assert_equal(6, sum)
  end
end