Struggling with variable arguments to block

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

Which version of ruby are you using?

I copied, pasted and got no errors with the mswin32 1.8.0 build.

  • Dan

Gavin Sinclair wrote:

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.

Actually it is the other way around - the second ``sum’’
is issuing the warning and first runs perfectly okay!

Here is a possible solution by distinguishing between
``normal’’ enumerables and enumerables yielding
possibly several values at the same time (e.g. hashes).

···

module Enumerable
def sum
result = 0
each { |elt| result += yield(elt) }
result
end

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

class Hash
include Enumerable::Many
end

sum of second elements

h = Hash[1,2,3,4]
p h.sum { |a,b| b } # 6
p h.to_a.sum {|a,b| b } # 6

This script should run danty without warnings.

/Christoph

Hi,

···

In message “Struggling with variable arguments to block” on 03/10/24, “Gavin Sinclair” gsinclair@soyabean.com.au writes:

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.

I smell something wrong. Hmm…

I get it. Hash#each should have passed single value to the block,
unlike Hash#each_pair. It will be fixed soon.

						matz.

c

Dan wrote:

Which version of ruby are you using?

Ah, good question.

ruby 1.8.0 (2003-09-01) [i386-cygwin]

I copied, pasted and got no errors with the mswin32 1.8.0 build.

Thanks for the report. I’ll try the very latest version.

Gavin

Dan Doel wrote:

Which version of ruby are you using?

I copied, pasted and got no errors with the mswin32 1.8.0 build.

  • Dan

Weird,

I did the same (with yesterdays cvs mswin32 build) and got
the error. IMO it is erroneous Ruby code anyway - maybe the
following snippet is somewhat illuminating.

···

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

public :sum

un_mod = [[1,2],[3,4]]
mod = un_mod.clone

def mod.each
super {|e| yield *e }
end

mod.sum { |a,b| b } # runs fine
p “###”
un_mod.sum { |a,b| b } # gives an error.

this results in

[1, 2]
[3, 4]
“###”
[[1, 2]]
D:/rb75.tmp:3:in +': nil can't be coerced into Fixnum (TypeError) from D:/rb75.tmp:3:in sum’


/Christoph

Please send off list mail to
‘my_mail@gmy.net’.gsub(/y/,‘x’)

Yukihiro Matsumoto wrote:

I get it. Hash#each should have passed single value to the
block, unlike Hash#each_pair. It will be fixed soon.

You mean Hash#each and Hash::each_pair will behave
differently after the fix??

···

/Christoph

Please send off list mail to
‘my_mail@gmy.net’.gsub(/y/,‘x’)

Hi,

···

In message “Re: Struggling with variable arguments to block” on 03/10/24, “Christoph” chr_mail@gmx.net writes:

You mean Hash#each and Hash::each_pair will behave
differently after the fix??

Yes.

						matz.

What will Hash#each behave like?

I’ve got a lot of code that simply uses Hash#each expecting:

hash.each { |k, v| … }

Will Hash#each behave like Hash#each_key ? Or will it be something different
altogether?

-austin

···

On Sat, 25 Oct 2003 00:03:32 +0900, Yukihiro Matsumoto wrote:

You mean Hash#each and Hash::each_pair will behave
differently after the fix??
Yes.


austin ziegler * austin@halostatue.ca * Toronto, ON, Canada
software designer * pragmatic programmer * 2003.10.24
* 11.48.16

Hi,

···

In message “Re: Struggling with variable arguments to block” on 03/10/25, Austin Ziegler austin@halostatue.ca writes:

You mean Hash#each and Hash::each_pair will behave
differently after the fix??
Yes.

What will Hash#each behave like?

The only notable change will be that hash.each{|*a| p a} will produce
[[k,v]], not [k,v]. Most code will run without any change.

						matz.

Hi –

Hi,

You mean Hash#each and Hash::each_pair will behave
differently after the fix??
Yes.

What will Hash#each behave like?

The only notable change will be that hash.each{|*a| p a} will produce
[[k,v]], not [k,v]. Most code will run without any change.

I confess to confusion about the new arg/param semantics. For example:

irb(main):005:0> *a = 1,2
=> [1, 2]
irb(main):006:0> *a = [1,2]
=> [1, 2]
irb(main):007:0> *a = [[1,2]]
=> [[[1, 2]]]

The one that doesn’t seem to occur at all is [[1,2]] – but that’s
what the new Hash#each will give… but aren’t block semantics modeled
on assignment semantics? (I’m also still puzzled about Proc.new {}
and proc {} being different from each other, but I’ll settle for
understanding |*a| for now :slight_smile:

David

···

On Sat, 25 Oct 2003, Yukihiro Matsumoto wrote:

In message “Re: Struggling with variable arguments to block” > on 03/10/25, Austin Ziegler austin@halostatue.ca writes:


David Alan Black
home: dblack@superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

I confess to confusion about the new arg/param semantics. For example:

  irb(main):005:0> *a = 1,2
  => [1, 2]
  irb(main):006:0> *a = [1,2]
  => [1, 2]

svg% ./ruby -ve '*a = 1,2; p a; *a = [1,2]; p a; *a = [[1,2]]; p a'
ruby 1.8.0 (2003-10-24) [i686-linux]
[1, 2]
[[1, 2]]
[[[1, 2]]]
svg%

Guy Decoux

dblack wrote:

I confess to confusion about the new arg/param semantics.
For example:

Me too - but I start seeing some light:-) Here is what
I got playing with this stuff (I guess you need a recent
cvs version …)

···

module Enumerable

def each_one
# turn an old style Hash#each
# into a new style Hash#each
each {|*e| yield *[e] }
end

def each_many
# reverse of the above
each {|e| yield *e }
end

def sum
result = 0
each { |e| result += yield e }
result
end

def sum_one
result = 0
each_one { |e| result += yield e }
result
end

def sum_many
result = 0
each_many { |e| result += yield *e }
result
end

end

class << n = Object.new

new style Hash::each

include Enumerable
def each
yield [1,2]
yield [3,4]
end
end

class << o = Object.new

old style Hash::each

include Enumerable
def each
yield 1,2
yield 3,4
end
end

n.sum {|a,b| b } # fine
o.sum_one {|a,b| b } # fine
o.sum {|a,b| b } # warning
o.sum_many {|a,b| b } # warning

n.sum_one {|a,b| b } # Error …

n.sum_many {|a,b| b } # warning

irb(main):005:0> *a = 1,2
=> [1, 2]
irb(main):006:0> *a = [1,2]
=> [1, 2]
irb(main):007:0> *a = [[1,2]]
=> [[[1, 2]]]

The one that doesn’t seem to occur at all is [[1,2]] – but
that’s what the new Hash#each will give… but aren’t block
semantics modeled on assignment semantics? (I’m also still
puzzled about Proc.new {} and proc {} being different from
each other, but I’ll settle for understanding |*a| for now :slight_smile:

I guess I’ll settle for understanding the wisdom behind the recent

enum.inject {|a,b| break “me” if … }
enum.collect {|a| break “me” if … }

change:-)


/Christoph

Please send off list mail to
‘my_mail@gmy.net’.gsub(/y/,‘x’)

Hi –

I confess to confusion about the new arg/param semantics. For example:

irb(main):005:0> *a = 1,2
=> [1, 2]
irb(main):006:0> *a = [1,2]
=> [1, 2]

svg% ./ruby -ve ‘*a = 1,2; p a; *a = [1,2]; p a; *a = [[1,2]]; p a’
ruby 1.8.0 (2003-10-24) [i686-linux]
[1, 2]
[[1, 2]]
[[[1, 2]]]

OK… that’s a start :slight_smile: Just so you know I wasn’t being
totally frivolous:

irb(main):001:0> *a = 1,2
=> [1, 2]
irb(main):002:0> *a = [1,2]
=> [1, 2]
irb(main):003:0> VERSION
=> “1.8.0”

so I guess this is a case where the irb parser hasn’t caught up?

David

···

On Sat, 25 Oct 2003, ts wrote:


David Alan Black
home: dblack@superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

throws hands up in air

I grabbed the latest CVS, noted that Matz had fixed hash.c just
recently, tried to compile, and couldn’t get past the second step.
“autoconf” worked, “./configure” didn’t, with the most mundane error
message:

configure: error: sources are in ., but `cd .’ does not work

Anyway, onto other things…

Gavin

···

On Saturday, October 25, 2003, 5:48:22 AM, Christoph wrote:

dblack wrote:

I confess to confusion about the new arg/param semantics.
For example:

Me too - but I start seeing some light:-) Here is what
I got playing with this stuff (I guess you need a recent
cvs version …)

Hi,

OK… that’s a start :slight_smile: Just so you know I wasn’t being
totally frivolous:

irb(main):001:0> *a = 1,2
=> [1, 2]
irb(main):002:0> *a = [1,2]
=> [1, 2]
irb(main):003:0> VERSION
=> “1.8.0”

so I guess this is a case where the irb parser hasn’t caught up?

This is what I get

irb(main):001:0> RUBY_RELEASE_DATE
=> “2003-10-24”
irb(main):002:0> *a = 1,2
=> [1, 2]
irb(main):003:0> *a = [1,2]
=> [[1, 2]]
irb(main):004:0> *a = [[1,2]]
=> [[[1, 2]]]

···

In message “Re: Struggling with variable arguments to block” on 03/10/25, dblack@superlink.net dblack@superlink.net writes:

Yukihiro Matsumoto wrote:

This is what I get

irb(main):001:0> RUBY_RELEASE_DATE
=> “2003-10-24”
irb(main):002:0> *a = 1,2
=> [1, 2]
irb(main):003:0> *a = [1,2]
=> [[1, 2]]
irb(main):004:0> *a = [[1,2]]
=> [[[1, 2]]]

irb(main):001:0> *a = 1,2
[1, 2]
irb(main):002:0> *a = [1,2]
[1, 2]
irb(main):003:0> *a = [[1,2]]
[[[1, 2]]]
irb(main):006:0> RUBY_RELEASE_DATE
“2003-08-04”
irb(main):007:0> RUBY_VERSION
“1.8.0”

This is using an RPM ruby binary versioned: ruby-1.8.0-1

ben@magneto% rpm -qi ruby ~
Name : ruby Relocations: (not relocateable)
Version : 1.8.0 Vendor: Red Hat, Inc.
Release : 1 Build Date: Tue 05 Aug 2003
11:17:30 AM EDT
Install Date: Thu 18 Sep 2003 01:02:38 PM EDT Build Host:
bugs.devel.redhat.com
Group : Development/Languages Source RPM: ruby-1.8.0-1.src.rpm
Size : 136668 License: Distributable
Signature : DSA/SHA1, Tue 09 Sep 2003 04:53:41 PM EDT, Key ID
fd372689897da07a
Packager : Red Hat, Inc. http://bugzilla.redhat.com/bugzilla

Ben