Request for two methods in Array class

Two small requests:

1. A new method, Array.combine (needs a better name for general use).
   It takes entries from two (or many) arrays and combines them.
   (like a combination of Array.zip, fetch, and map)

   For my use, I needed multiplication, but a block would be more general.

        a = [1,2,3]
        b = [1,3,5]
        a.combine(b) => [1,6,15]

   Or with a block:

        a.combine(b) {|x,y| x*y}
  
   The new method would replace this sequence:

      a.zip(b).map {|e| e[0]*e[1]}
    or
      a.zip(b,c,d).map {|e| e.inject(1) {|prod, n| prod*n} }

2. A block initializer for Array.new, like that for Hash.new.
   Good for self-initializing a table of recurrence relations.
   
    a = Array.new() {|self, i| 2*self[i-1] - self[i-2]}

    a[0] = 1 # define the first two ...
    a[1] = x

    a[2] => 2x-1 # the rest are derived on the fly as needed
    a[3] => ...

Hopefully, I'm not blind and haven't missed some obvious use
of the existing methods. My apologies if that's the case.

Whatever... just some thoughts. Are they worth adding?

Thanks!

Or perhaps:

c = a.weave(b)
c #=> [[1, 1], [2, 3], [3, 5]]

...then you could:

c.collect! do {|item| item[0] * item[1]}

c #=> [1, 6, 15]

  Sean O'Dell

···

On Monday 02 August 2004 12:26, Mike Hall wrote:

1. A new method, Array.combine (needs a better name for general use).
   It takes entries from two (or many) arrays and combines them.
   (like a combination of Array.zip, fetch, and map)

   For my use, I needed multiplication, but a block would be more general.

        a = [1,2,3]
        b = [1,3,5]
        a.combine(b) => [1,6,15]

   Or with a block:

        a.combine(b) {|x,y| x*y}

Mike Hall wrote:

Two small requests:

1. A new method, Array.combine (needs a better name for general use).
   It takes entries from two (or many) arrays and combines them.
   (like a combination of Array.zip, fetch, and map)

   For my use, I needed multiplication, but a block would be more general.

        a = [1,2,3]
        b = [1,3,5]
        a.combine(b) => [1,6,15]

   Or with a block:

        a.combine(b) {|x,y| x*y}
     The new method would replace this sequence:

      a.zip(b).map {|e| e[0]*e[1]}

It's the same length:

  a.zip(b).map {|x,y| x*y}
  # vs.
  a.combine(b) {|x,y| x*y}

But I must say, that I like the name combine a lot.

Regards,

   Michael

Hi,

1. A new method, Array.combine (needs a better name for general use).
  It takes entries from two (or many) arrays and combines them.
  (like a combination of Array.zip, fetch, and map)

Sounds nice. But I'm not sure "combine" is a proper name for the
method. We need more discussion, for example:

  * the best name for the behavior.
  * whether it takes only one argument or any number of arguments.
  * whether "multiplication" should be the default.

2. A block initializer for Array.new, like that for Hash.new.
  Good for self-initializing a table of recurrence relations.

Array.new already takes a block, with little bit different behavior
than what you've describe. In addition,

   a = Array.new() {|self, i| 2*self[i-1] - self[i-2]}

I assume this accesses self[-1] and self[-2] during the array
initialization, which seem no good. Perhaps there should be better
way to express what you want.

              matz.

···

In message "Request for two methods in Array class" on 04/08/03, Mike Hall <mghallNO@SPAMenteract.com> writes:

Mike Hall wrote:

1. A new method, Array.combine (needs a better name for general use).
   It takes entries from two (or many) arrays and combines them.
   (like a combination of Array.zip, fetch, and map)

Following on my suggestion that combine is just an extension of map, how do you feel about this code?:

class Array
     alias :__map :map
     def map *args, &block
         identity = proc{ |*items| items.length==1 ? items[0] : items }
         self.zip(*args).__map{ |line| (block or identity).call *line }
     end
     alias :collect :map
end

irb(main):003:0> [1,2,3].map
=> [1, 2, 3]
irb(main):004:0> [1,2,3].map [1,3,5]
=> [[1, 1], [2, 3], [3, 5]]
irb(main):005:0> [1,2,3].map([1,3,5]){|x,y|x*y}
=> [1, 6, 15]

For "combine":
How much of a performance problem would it be to have zip, if given a block, return an array of the results, instead of nil? Code using it for side-effects at the moment would be unaffected (but it would create a potentially big array). Then you could do
[1,2,3].zip([4,5,6]) # => [[1,4],[2,5],[3,6]]

[1,2,3].zip([4,5,6]) { |x,y| puts x*y }
> 4
> 10
> 18
# => [nil,nil,nil] (this would presumably be ignored)

[1,2,3].zip([4,5,6]) { |x,y| x*y } # => [4,10,18]

This generalises to more than one array...

Sam

It's the same length:
a.zip(b).map {|x,y| x*y}
# vs.
a.combine(b) {|x,y| x*y}

but one less object creation

But I must say, that I like the name combine a lot.

ditto.

-a

···

On Tue, 3 Aug 2004, Michael Neumann wrote:
--

EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
PHONE :: 303.497.6469
A flower falls, even though we love it;
and a weed grows, even though we do not love it. --Dogen

===============================================================================

Yukihiro Matsumoto wrote:

>1. A new method, Array.combine (needs a better name for general use).
> It takes entries from two (or many) arrays and combines them.
> (like a combination of Array.zip, fetch, and map)

I don't think we need another method for this. This sounds like the domain of Enumerable#map (at least as far as how map works in lisp and python).

Is there anything about map that prevents it from being extended to accept 0 or more enumerables as parameters, then a block?

According to this rationale:

  * the best name for the behavior.

map

  * whether it takes only one argument or any number of arguments.

0 or more. (Much more powerful, and we don't have to give up anything about only taking one arg to support taking many)

  * whether "multiplication" should be the default.

If by "default" you mean "when no block is passed", as I see it, the default should be identical or similar to zip.

in python, map with multiple lists and no function returns a list with the length of the largest list, each element being a list of the elements at its index in the lists passed in, None is used for elements that don't exist in the other lists.

I like python's behavior, but I think in the case of ruby, using the length of the receiver better matches expected behavior than using a length from one of the parameters.

(on the other hand, using the longest length makes it more useful in that it differs from zip, and it seems more useful in most cases)

"Yukihiro Matsumoto" <matz@ruby-lang.org> schrieb im Newsbeitrag
news:1091510011.625923.536.nullmailer@picachu.netlab.jp...

Hi,

>1. A new method, Array.combine (needs a better name for general use).
> It takes entries from two (or many) arrays and combines them.
> (like a combination of Array.zip, fetch, and map)

Sounds nice. But I'm not sure "combine" is a proper name for the
method. We need more discussion, for example:

  * the best name for the behavior.

I like "combine" but I'm open to suggestions.

  * whether it takes only one argument or any number of arguments.

IMHO n>=0

  * whether "multiplication" should be the default.

No IMHO.

I imageine behavior similar to this:

class Array
  def combine(*arrays, &b)
    result =
    each_with_index {|e,idx| result << b.call(e, *arrays.map {|ar|
ar[idx]})}
    result
  end
end

Note that arrays can contain anything that implements , which might even
be a lambda (aka function):

a = [1,2,3]
a.combine(lambda {|i| i*2}) {|x,y| x*y}
=> [0, 4, 12]

So "arrays" might not be the ideal argument name.

Thinking a bit further about this it seems to me that the method might be
better put into Enumerable or Array as module method since arguments seem
to be symmetric:

class Array
  def self.combine(*arrays, &b)
    limit = arrays.inject(0){|l,a| s=a.size; s>l ? s : l}
    result = Array.new limit
    limit.times {|i| result[i] = b.call(*arrays.map {|ar| ar[i]}) }
    result
  end
end

irb(main):054:0> Array.combine([1,2,3],[1,3,5]) {|x,y| x*y}
=> [1, 6, 15]
irb(main):055:0> Array.combine([1,2,3],[1,3,5]) {|*x| x.inject(1){|z,i|
z*i} }
=> [1, 6, 15]

Kind regards

    robert

···

In message "Request for two methods in Array class" > on 04/08/03, Mike Hall <mghallNO@SPAMenteract.com> writes:

Yukihiro Matsumoto wrote:

Hi,

>1. A new method, Array.combine (needs a better name for general use).
> It takes entries from two (or many) arrays and combines them.
> (like a combination of Array.zip, fetch, and map)

Sounds nice. But I'm not sure "combine" is a proper name for the
method. We need more discussion, for example:

  * the best name for the behavior.

combine sounds nice to me. it "combinates" two or more arrays (or enumerables) into one. but one might argue against combine as name, as it it's not clear that combine returns an array that is not larger than it's arguments joined together.

IMO, combine should be equal to zip.map:

   enum.combine(enum2, enum3,...) { block }
   # equal
   enum.zip(enum2, enum3,...).map { block }

  * whether it takes only one argument or any number of arguments.

many

  * whether "multiplication" should be the default.

no. default should be "{|arr| arr}" (the default of map)

Regards,

   Michael

···

In message "Request for two methods in Array class" > on 04/08/03, Mike Hall <mghallNO@SPAMenteract.com> writes:

"Yukihiro Matsumoto" <matz@ruby-lang.org> wrote in message
news:1091510011.625923.536.nullmailer@picachu.netlab.jp...

Hi,

>1. A new method, Array.combine (needs a better name for general use).
> It takes entries from two (or many) arrays and combines them.
> (like a combination of Array.zip, fetch, and map)

Sounds nice. But I'm not sure "combine" is a proper name for the
method. We need more discussion, for example:

Hi Matz !

I'm using Ruby 1.6.8.
Maybe my suggestions are already part of some later version,
or can be upgraded to some more fittable behaviour.

  * the best name for the behavior.

--> match

  * whether it takes only one argument or any number of arguments.

--> one or more

  * whether "multiplication" should be the default.

If no (or an empty) block is given, what would be the most common way to
'match' array elements ?
Regarding to Sean O'Dells 'weave example', I think it's like this:

natural = [1,2,3]
odd = [1,3,5]
even = [2,4,6]

matched = natural.match(odd,even)
matched #=> [[1, 1, 2], [2, 3, 4], [3, 5, 6]]

Greetings,
Chris

<snip>

···

In message "Request for two methods in Array class" > on 04/08/03, Mike Hall <mghallNO@SPAMenteract.com> writes:

Hi,

>1. A new method, Array.combine (needs a better name for general use).
> It takes entries from two (or many) arrays and combines them.
> (like a combination of Array.zip, fetch, and map)

Sounds nice. But I'm not sure "combine" is a proper name for the
method. We need more discussion, for example:

  * the best name for the behavior.
  * whether it takes only one argument or any number of arguments.

Any number, I'd say

  * whether "multiplication" should be the default.

Almost certainly not.

Getting a bit complicated, but what I'd like is to check if the last or
two last args are a symbol and an optional seed, which are injected
across the current tuple.

e.g. the multiplication case would be a.combine(b,c,d, :*) which would mean
a.zip(b,c,d) {|*args| args.inject {|a,v| a.send(:*, v)}}

If both a symbol and a block are provided, the symbol is injected first
and the result passed to the block.

Default (no symbol and no block) should be to just zip the arrays.

martin

···

Yukihiro Matsumoto <matz@ruby-lang.org> wrote:

In message "Request for two methods in Array class" > on 04/08/03, Mike Hall <mghallNO@SPAMenteract.com> writes:

Hi --

Hi,

>1. A new method, Array.combine (needs a better name for general use).
> It takes entries from two (or many) arrays and combines them.
> (like a combination of Array.zip, fetch, and map)

Sounds nice. But I'm not sure "combine" is a proper name for the
method. We need more discussion, for example:

I know I'm being the voice of possibly excessive conservatism
here... but I'm just wondering what this method would give us, since
we can already do zip+map. As Ara said, it would create one less
object. But chaining methods always creates intermediate objects. If
zip+map deserves to be combined into a new, single method, what about
flatten+map, or compact+map? And then (flatten+map)+compact....

I realize that zip+map is more likely to be actually used, and people
have often said they wish zip's block mapped the elements. But I
still wonder whether it's worth a new method and method name.

David

···

On Tue, 3 Aug 2004, Yukihiro Matsumoto wrote:

In message "Request for two methods in Array class" > on 04/08/03, Mike Hall <mghallNO@SPAMenteract.com> writes:

--
David A. Black
dblack@wobblini.net

It's the same length:
a.zip(b).map {|x,y| x*y}
# vs.
a.combine(b) {|x,y| x*y}

but one less object creation

But I must say, that I like the name combine a lot.

ditto.

Seems like a good idea. After reading the chapter of Meyers book Lothar sent, the idea seems even better.
Another name idea is collect_wtih
c = a.collect_with(b) { |x,y| x*y }
a.collect_with!(b) { |x,y| x*y } # looks bad?
d = a.collect_with(b, c) { |x,y,z| x*y*z } # multiple params would be useful
-Charlie

···

On Aug 2, 2004, at 1:21 PM, Ara.T.Howard wrote:

On Tue, 3 Aug 2004, Michael Neumann wrote:

-a
--

> EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
> PHONE :: 303.497.6469
> A flower falls, even though we love it;
> and a weed grows, even though we do not love it. | --Dogen

"Ara.T.Howard" <ahoward@noaa.gov> schrieb im Newsbeitrag
news:Pine.LNX.4.60.0408021412540.11770@harp.ngdc.noaa.gov...

···

On Tue, 3 Aug 2004, Michael Neumann wrote:

> It's the same length:
> a.zip(b).map {|x,y| x*y}
> # vs.
> a.combine(b) {|x,y| x*y}

but one less object creation

You can save that by using inject:

a = [1,2,3]
b = [1,3,5]
a.inject(){|arr,x| arr << (x*b[arr.size])}

Regards

    robert

That's good thinking. The most compelling examples so far have been
the ones where #zip actually has map-like functionality. No need for
a new method. *Definitely* don't touch #map !!

  arr.zip { ... } # same behaviour as arr.map { ... }
  arr.zip(x) { ... } # " " " " arr.zip(x).map { ... }
  etc.
  
Gavin

···

On Tuesday, August 3, 2004, 10:59:57 PM, David wrote:

Hi --

On Tue, 3 Aug 2004, Yukihiro Matsumoto wrote:

Hi,

In message "Request for two methods in Array class" >> on 04/08/03, Mike Hall <mghallNO@SPAMenteract.com> writes:

>1. A new method, Array.combine (needs a better name for general use).
> It takes entries from two (or many) arrays and combines them.
> (like a combination of Array.zip, fetch, and map)

Sounds nice. But I'm not sure "combine" is a proper name for the
method. We need more discussion, for example:

I know I'm being the voice of possibly excessive conservatism
here... but I'm just wondering what this method would give us, since
we can already do zip+map. As Ara said, it would create one less
object. But chaining methods always creates intermediate objects. If
zip+map deserves to be combined into a new, single method, what about
flatten+map, or compact+map? And then (flatten+map)+compact....

I realize that zip+map is more likely to be actually used, and people
have often said they wish zip's block mapped the elements. But I
still wonder whether it's worth a new method and method name.

How about (snipped from Sean's e-mail earlier)

a.weave(b)
#=> [[1, 1], [2, 3], [3, 5]]

The default action would be to create the larger array, but a block
could be used to prevent the array from growing, aka

a.weave(b) {|x,y| x*y}
#=> [1, 6, 15]

···

On Tue, 3 Aug 2004 18:32:31 +0900, Michael Neumann <mneumann@ntecs.de> wrote:

Yukihiro Matsumoto wrote:
> Hi,
>
> In message "Request for two methods in Array class" > > on 04/08/03, Mike Hall <mghallNO@SPAMenteract.com> writes:
>
> >1. A new method, Array.combine (needs a better name for general use).
> > It takes entries from two (or many) arrays and combines them.
> > (like a combination of Array.zip, fetch, and map)
>
> Sounds nice. But I'm not sure "combine" is a proper name for the
> method. We need more discussion, for example:
>
> * the best name for the behavior.

combine sounds nice to me. it "combinates" two or more arrays (or
enumerables) into one. but one might argue against combine as name, as
it it's not clear that combine returns an array that is not larger than
it's arguments joined together.

IMO, combine should be equal to zip.map:

   enum.combine(enum2, enum3,...) { block }
   # equal
   enum.zip(enum2, enum3,...).map { block }

> * whether it takes only one argument or any number of arguments.

many

> * whether "multiplication" should be the default.

no. default should be "{|arr| arr}" (the default of map)

Regards,

   Michael

That's a strong point. Elaborating a bit more on that, perhaps what is
needed is not a new method (or a collection of them), but a way of
piping objects instead of creating intermediate ones. Then methods
could be still chained, but with better spatial efficiency.
   -CWS

···

On Tue, 3 Aug 2004 21:59:57 +0900, David A. Black <dblack@wobblini.net> wrote:

Hi --

On Tue, 3 Aug 2004, Yukihiro Matsumoto wrote:

> Hi,
>
> In message "Request for two methods in Array class" > > on 04/08/03, Mike Hall <mghallNO@SPAMenteract.com> writes:
>
> >1. A new method, Array.combine (needs a better name for general use).
> > It takes entries from two (or many) arrays and combines them.
> > (like a combination of Array.zip, fetch, and map)
>
> Sounds nice. But I'm not sure "combine" is a proper name for the
> method. We need more discussion, for example:

I know I'm being the voice of possibly excessive conservatism
here... but I'm just wondering what this method would give us, since
we can already do zip+map. As Ara said, it would create one less
object. But chaining methods always creates intermediate objects. If
zip+map deserves to be combined into a new, single method, what about
flatten+map, or compact+map? And then (flatten+map)+compact....

I realize that zip+map is more likely to be actually used, and people
have often said they wish zip's block mapped the elements. But I
still wonder whether it's worth a new method and method name.

David

--
David A. Black
dblack@wobblini.net

It's the same length:
a.zip(b).map {|x,y| x*y}
# vs.
a.combine(b) {|x,y| x*y}

but one less object creation

But I must say, that I like the name combine a lot.

ditto.

Seems like a good idea. After reading the chapter of Meyers book Lothar sent, the idea seems even better.
Another name idea is collect_wtih
c = a.collect_with(b) { |x,y| x*y }
a.collect_with!(b) { |x,y| x*y } # looks bad?
d = a.collect_with(b, c) { |x,y,z| x*y*z } # multiple params would be useful

Looks like this has been talked about by others:
http://www.ai.mit.edu/~gregs/ll1-discuss-archive-html/msg03386.html
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/29671

still seems useful to me though
c = a.collect_with(keys) { ... }

···

On Aug 2, 2004, at 6:51 PM, Charles Mills wrote:

On Aug 2, 2004, at 1:21 PM, Ara.T.Howard wrote:

On Tue, 3 Aug 2004, Michael Neumann wrote:

-Charlie

-a
--

> EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
> PHONE :: 303.497.6469
> A flower falls, even though we love it;
> and a weed grows, even though we do not love it. | --Dogen

Hi --

···

On Tue, 3 Aug 2004, Gavin Sinclair wrote:

On Tuesday, August 3, 2004, 10:59:57 PM, David wrote:

> Hi --

> On Tue, 3 Aug 2004, Yukihiro Matsumoto wrote:

>> Hi,
>>
>> In message "Request for two methods in Array class" > >> on 04/08/03, Mike Hall <mghallNO@SPAMenteract.com> writes:
>>
>> >1. A new method, Array.combine (needs a better name for general use).
>> > It takes entries from two (or many) arrays and combines them.
>> > (like a combination of Array.zip, fetch, and map)
>>
>> Sounds nice. But I'm not sure "combine" is a proper name for the
>> method. We need more discussion, for example:

> I know I'm being the voice of possibly excessive conservatism
> here... but I'm just wondering what this method would give us, since
> we can already do zip+map. As Ara said, it would create one less
> object. But chaining methods always creates intermediate objects. If
> zip+map deserves to be combined into a new, single method, what about
> flatten+map, or compact+map? And then (flatten+map)+compact....

> I realize that zip+map is more likely to be actually used, and people
> have often said they wish zip's block mapped the elements. But I
> still wonder whether it's worth a new method and method name.

That's good thinking. The most compelling examples so far have been
the ones where #zip actually has map-like functionality. No need for
a new method. *Definitely* don't touch #map !!

  arr.zip { ... } # same behaviour as arr.map { ... }
  arr.zip(x) { ... } # " " " " arr.zip(x).map { ... }
  etc.

The problem with having zip do auto-mapping is that you lose the
ability to do zip with a block but without mapping (which might be
desireable for performance or other reasons). Whereas if it stays the
way it is, you can always map the results.

David

--
David A. Black
dblack@wobblini.net