[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

each by arity

Trans

6/14/2009 7:30:00 PM

I've always wondered, why?

c = []
[1,2,3,4].each{ |x,y| c << [x, y] }
c

gives us

[ [1,nil], [2,nil], [3,nil], [4,nil] ]

why not allow it to look at the arity of the block? And thus produce

[ [1,2], [3,4] ]

I don't see how the former is ever of any use, but the later certainly
is. am i missing something obvious?

T.



11 Answers

Tony Arcieri

6/14/2009 7:35:00 PM

0

[Note: parts of this message were removed to make it a legal post.]

On Sun, Jun 14, 2009 at 1:29 PM, Trans <transfire@gmail.com> wrote:

> I don't see how the former is ever of any use, but the later certainly
> is. am i missing something obvious?
>

The main use I've seen is for iterating hashes, e.g.:

>> {:a => 1, :b => 2, :c => 3}.map { |k, v| "#{k}=#{v}" }.join(",")
=> "b=2,c=3,a=1"

Of course you could always massign against an incoming array, so this case
is also covered by:

>> {:a => 1, :b => 2, :c => 3}.map { |(k, v)| "#{k}=#{v}" }.join(",")
=> "b=2,c=3,a=1"

The massign approach is nice with methods where you want to explode an array
as arguments alongside other arguments:

>> {:a => 1, :b => 2, :c => 3}.inject(0) { |n, (k, v)| n + v }
=> 6

Personally I'm not a fan of this mode of arity handling and think massign
and splats cover all the cases where it's useful.

--
Tony Arcieri
medioh.com

Joel VanderWerf

6/14/2009 7:38:00 PM

0

Trans wrote:
> I've always wondered, why?
>
> c = []
> [1,2,3,4].each{ |x,y| c << [x, y] }
> c
>
> gives us
>
> [ [1,nil], [2,nil], [3,nil], [4,nil] ]
>
> why not allow it to look at the arity of the block? And thus produce
>
> [ [1,2], [3,4] ]
>
> I don't see how the former is ever of any use, but the later certainly
> is. am i missing something obvious?

What would it do with

[ [1,2], [3,4] ].each {|x,y| ... }

--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

Tony Arcieri

6/14/2009 8:33:00 PM

0

[Note: parts of this message were removed to make it a legal post.]

On Sun, Jun 14, 2009 at 1:37 PM, Joel VanderWerf <vjoel@path.berkeley.edu>wrote:

> What would it do with
>
> [ [1,2], [3,4] ].each {|x,y| ... }
>

One iteration, with:

x = [1,2]
y = [3,4]

This could be expanded out with:

[ [1,2], [3,4] ].each {|(a,b),(c,d)| ... }

and

[ [1,2], [3,4] ].each {|(x,y)| ... }

still provides the old behavior.

This effectively provides the same behavior as each_slice, without the need
for a separate function, and having the "n" argument of each_slice "implied"
in the arity of the block.

These are the cool little bits of syntactic sugar I love to see in Ruby, but
I often find trying to do things with subtle little idiosyncrasies can make
code confusing.

each_slice is a lot clearer.

--
Tony Arcieri
medioh.com

Yossef Mendelssohn

6/14/2009 8:43:00 PM

0

On Jun 14, 3:32=A0pm, Tony Arcieri <t...@medioh.com> wrote:
> On Sun, Jun 14, 2009 at 1:37 PM, Joel VanderWerf <vj...@path.berkeley.edu=
>wrote:
>
> > What would it do with
>
> > [ [1,2], [3,4] ].each {|x,y| ... }
>
> One iteration, with:
>
> x =3D [1,2]
> y =3D [3,4]
>
> This could be expanded out with:
>
> [ [1,2], [3,4] ].each {|(a,b),(c,d)| ... }
>
> and
>
> [ [1,2], [3,4] ].each {|(x,y)| ... }
>
> still provides the old behavior.

And by "the old behavior" I presume you mean two iterations:

x =3D 1
y =3D 2

x =3D 3
y =3D 4

And how is that going to happen based on inspecting arity? At least
with 1.8.6, Proc.new {|x, y| } and Proc.new {|(x, y)| } both have an
arity of 2.

> each_slice is a lot clearer.

Yes. Yes, it is.

--
-yossef

Tony Arcieri

6/14/2009 9:34:00 PM

0

[Note: parts of this message were removed to make it a legal post.]

On Sun, Jun 14, 2009 at 2:43 PM, Yossef Mendelssohn <ymendel@pobox.com>wrote:

> And by "the old behavior" I presume you mean two iterations:


Correct


> And how is that going to happen based on inspecting arity? At least
> with 1.8.6, Proc.new {|x, y| } and Proc.new {|(x, y)| } both have an
> arity of 2.
>

So it does. Unfortunate.

--
Tony Arcieri
medioh.com

Trans

6/15/2009 4:22:00 AM

0



On Jun 14, 4:43=A0pm, Yossef Mendelssohn <ymen...@pobox.com> wrote:
> On Jun 14, 3:32=A0pm, Tony Arcieri <t...@medioh.com> wrote:
>
>
>
> > On Sun, Jun 14, 2009 at 1:37 PM, Joel VanderWerf <vj...@path.berkeley.e=
du>wrote:
>
> > > What would it do with
>
> > > [ [1,2], [3,4] ].each {|x,y| ... }
>
> > One iteration, with:
>
> > x =3D [1,2]
> > y =3D [3,4]
>
> > This could be expanded out with:
>
> > [ [1,2], [3,4] ].each {|(a,b),(c,d)| ... }
>
> > and
>
> > [ [1,2], [3,4] ].each {|(x,y)| ... }
>
> > still provides the old behavior.
>
> And by "the old behavior" I presume you mean two iterations:
>
> =A0 x =3D 1
> =A0 y =3D 2
>
> =A0 x =3D 3
> =A0 y =3D 4
>
> And how is that going to happen based on inspecting arity? At least
> with 1.8.6, Proc.new {|x, y| } and Proc.new {|(x, y)| } both have an
> arity of 2.

The underlying systems has to see the difference regardless. In fact
the current implementation has to do more, b/c it has to look at the
receiver itself and see that it contains arrays as elements in order
to know how to treat it, which is rather inefficient. (Also, I think
one could argue that either this arity is wrong, or the concept of
arity needs to be expanded with an added dimension.)

> > each_slice is a lot clearer.
>
> Yes. Yes, it is.

Slice by arity seems pretty clear to me. Moreover, where is
#map_slice? It starts to look a lot clearer when we think of adding
"_slice" to every Enumerable method.

Go ahead and call out the Enumerator now.

T.

Joshua Ballanco

6/15/2009 5:45:00 AM

0


On Jun 14, 2009, at 9:22 PM, trans wrote:

>
>
> On Jun 14, 4:43 pm, Yossef Mendelssohn <ymen...@pobox.com> wrote:
>> On Jun 14, 3:32 pm, Tony Arcieri <t...@medioh.com> wrote:
>>
>>
>>
>>> On Sun, Jun 14, 2009 at 1:37 PM, Joel VanderWerf
>>> <vj...@path.berkeley.edu>wrote:
>>
>>>> What would it do with
>>
>>>> [ [1,2], [3,4] ].each {|x,y| ... }
>>
>>> One iteration, with:
>>
>>> x = [1,2]
>>> y = [3,4]
>>
>>> This could be expanded out with:
>>
>>> [ [1,2], [3,4] ].each {|(a,b),(c,d)| ... }
>>
>>> and
>>
>>> [ [1,2], [3,4] ].each {|(x,y)| ... }
>>
>>> still provides the old behavior.
>>
>> And by "the old behavior" I presume you mean two iterations:
>>
>> x = 1
>> y = 2
>>
>> x = 3
>> y = 4
>>
>> And how is that going to happen based on inspecting arity? At least
>> with 1.8.6, Proc.new {|x, y| } and Proc.new {|(x, y)| } both have an
>> arity of 2.
>
> The underlying systems has to see the difference regardless. In fact
> the current implementation has to do more, b/c it has to look at the
> receiver itself and see that it contains arrays as elements in order
> to know how to treat it, which is rather inefficient. (Also, I think
> one could argue that either this arity is wrong, or the concept of
> arity needs to be expanded with an added dimension.)

At this point, it sounds more like we're talking about OCaml style
pattern matching than proc arity. I, for one, would love to see Ruby
gain some sort of pattern matching to work with elements, but I fear
this would be difficult to implement on top of Ruby's dynamism.

- Josh

Robert Klemme

6/15/2009 6:28:00 AM

0

On 15.06.2009 07:44, Joshua Ballanco wrote:
> On Jun 14, 2009, at 9:22 PM, trans wrote:
>
>>
>> On Jun 14, 4:43 pm, Yossef Mendelssohn <ymen...@pobox.com> wrote:
>>> On Jun 14, 3:32 pm, Tony Arcieri <t...@medioh.com> wrote:
>>>
>>>
>>>
>>>> On Sun, Jun 14, 2009 at 1:37 PM, Joel VanderWerf
>>>> <vj...@path.berkeley.edu>wrote:
>>>>> What would it do with
>>>>> [ [1,2], [3,4] ].each {|x,y| ... }
>>>> One iteration, with:
>>>> x = [1,2]
>>>> y = [3,4]
>>>> This could be expanded out with:
>>>> [ [1,2], [3,4] ].each {|(a,b),(c,d)| ... }
>>>> and
>>>> [ [1,2], [3,4] ].each {|(x,y)| ... }
>>>> still provides the old behavior.
>>> And by "the old behavior" I presume you mean two iterations:
>>>
>>> x = 1
>>> y = 2
>>>
>>> x = 3
>>> y = 4
>>>
>>> And how is that going to happen based on inspecting arity? At least
>>> with 1.8.6, Proc.new {|x, y| } and Proc.new {|(x, y)| } both have an
>>> arity of 2.
>> The underlying systems has to see the difference regardless. In fact
>> the current implementation has to do more, b/c it has to look at the
>> receiver itself and see that it contains arrays as elements in order
>> to know how to treat it, which is rather inefficient. (Also, I think
>> one could argue that either this arity is wrong, or the concept of
>> arity needs to be expanded with an added dimension.)

But the problem is that we have two steps here:
1. invocation of yield
2. distributing arguments across block parameters

If I followed the thread properly then step 1 would have to be
influenced by knowledge about the block while currently only step 2
does. The code invoking yield needs to be in charge yet at the moment
it only has arity as information. I believe more information about the
block needs to be provided otherwise this cannot work because #each
needs to know this or else it cannot decide how many elements from the
current Enumerable must be handed off to the block.

> At this point, it sounds more like we're talking about OCaml style
> pattern matching than proc arity. I, for one, would love to see Ruby
> gain some sort of pattern matching to work with elements, but I fear
> this would be difficult to implement on top of Ruby's dynamism.

1.9 is actually moving into that direction:

irb(main):001:0> f = lambda {|a,*b,c| p a,b,c}
=> #<Proc:0x100d08bc@(irb):1 (lambda)>
irb(main):002:0> f[1,2]
1
[]
2
=> [1, [], 2]
irb(main):003:0> f[1,2,3]
1
[2]
3
=> [1, [2], 3]
irb(main):004:0> f[1,2,3,4]
1
[2, 3]
4
=> [1, [2, 3], 4]
irb(main):005:0>

And even in 1.8 you had some level of pattern matching, e.g.

irb(main):001:0> h={1=>2,3=>4}
=> {1=>2, 3=>4}
irb(main):002:0> h.inject(0) {|s,(k,v)| s + k + v}
=> 10

Kind regards

robert

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestprac...

Yossef Mendelssohn

6/15/2009 6:17:00 PM

0

[Note: parts of this message were removed to make it a legal post.]

On Sun, Jun 14, 2009 at 11:22 PM, trans <transfire@gmail.com> wrote:

> On Jun 14, 4:43 pm, Yossef Mendelssohn <ymen...@pobox.com> wrote:
> > And how is that going to happen based on inspecting arity? At least
> > with 1.8.6, Proc.new {|x, y| } and Proc.new {|(x, y)| } both have an
> > arity of 2.
>
> The underlying systems has to see the difference regardless.


This is true. I was just pointing out that it's not arity that stores the
difference, and that makes this at least a little more difficult.


> (Also, I think one could argue that either this arity is wrong, or the
> concept of
> arity needs to be expanded with an added dimension.)


You can argue a lot of things, and I'm sure you want to. Personally, I have
yet to find the Ruby concept of block arity lacking, but then again I don't
try to do anything clever with it.


> > > each_slice is a lot clearer.
> >
> > Yes. Yes, it is.
>
> Slice by arity seems pretty clear to me. Moreover, where is
> #map_slice? It starts to look a lot clearer when we think of adding
> "_slice" to every Enumerable method.


Why is map_slice needed? What's the problem with calling each_slice and then
map on the result? Is it a case of optimization? Are you worried about the
resources you'll be using? If so, it's quite possible to use each_slice to
iterate over one collection while appending results to an array. But maybe
it's not functional enough for you. Or core enough.

Go ahead and call out the Enumerator now.


I have no trouble with requiring 'enumerator' and explicitly using exactly
the methods I want. I'd rather be clear than clever.

--
-yossef

Robert Klemme

6/16/2009 6:06:00 AM

0

On 15.06.2009 20:17, Yossef Mendelssohn wrote:
> [Note: parts of this message were removed to make it a legal post.]
>
> On Sun, Jun 14, 2009 at 11:22 PM, trans <transfire@gmail.com> wrote:

>> Slice by arity seems pretty clear to me. Moreover, where is
>> #map_slice? It starts to look a lot clearer when we think of adding
>> "_slice" to every Enumerable method.
>
> Why is map_slice needed? What's the problem with calling each_slice and then
> map on the result? Is it a case of optimization? Are you worried about the
> resources you'll be using? If so, it's quite possible to use each_slice to
> iterate over one collection while appending results to an array. But maybe
> it's not functional enough for you. Or core enough.
>
> Go ahead and call out the Enumerator now.
>
> I have no trouble with requiring 'enumerator' and explicitly using exactly
> the methods I want. I'd rather be clear than clever.

Question is whether it is clever to duplicate all methods with another
one that has "_slice" appended to the name. I personally favor the
approach with Enumerator, e.g. depending on version

enum.enum_for(:each_slice, 2).map {|a,b| ...}
enum.each_slice(2).map {|a,b| ...}

- especially since it works as easy for #each_cons and others. In other
words: this is much more modular.

Kind regards

robert

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestprac...