Parallel Array

If I have two arrays a=[1,1,1,1] and b=[2,2,2,2] and I want to add each
element up, something like -

c = a plus b => [3,3,3,3]

One way I could think up is -

irb(main):010:0> (0...a.length).collect{|k| a[k]+b[k]}
=> [3, 3, 3, 3]

Is there a better/faster way?

Thanks
Nasir

a.zip(b).collect{|x| x.inject{|q, r| q+r}}

···

On 7/5/06, Nasir Khan <rubylearner@gmail.com> wrote:

If I have two arrays a=[1,1,1,1] and b=[2,2,2,2] and I want to add each
element up, something like -

c = a plus b => [3,3,3,3]

One way I could think up is -

irb(main):010:0> (0...a.length).collect{|k| a[k]+b[k]}
=> [3, 3, 3, 3]

Is there a better/faster way?

Thanks
Nasir

--
Phillip Hutchings
http://www.sitharus.com/

This is probably the simplest:

# Arrays have constructors too!
c = Array.new(a.length) { |i| a[i] + b[i] }

These are more fun/complicated.

# initialise and fill in place.
c = .fill(0...a.length) { |i| a[i] + b[i] }

# use inject, it's good for job security.
c = a.zip(b).map { |x| x.inject(0) { |s, n| s + n } }

# using inject is fun. Let's do it again.
c = (0...a.length).inject() { |x, n| x << a[n] + b[n] }

I don't think there are any actual efficiency gains to be made, to tell the truth. The direct initialisation is possibly very slightly more efficient (fewer method calls, fewer intermediate arrays created), but I doubt that the difference would be distinguishable from noise in an actual application. The only reason I'm not 100% fond of the block constructor is that its semantics are quite different from the syntactically-similar Hash constructor with a default block, but that's a pretty subjective quibble.

matthew smillie.

···

On Jul 5, 2006, at 1:16, Nasir Khan wrote:

If I have two arrays a=[1,1,1,1] and b=[2,2,2,2] and I want to add each
element up, something like -

c = a plus b => [3,3,3,3]

One way I could think up is -

irb(main):010:0> (0...a.length).collect{|k| a[k]+b[k]}
=> [3, 3, 3, 3]

Is there a better/faster way?

fr Nasir:
# irb(main):010:0> (0...a.length).collect{|k| a[k]+b[k]}
# => [3, 3, 3, 3]

looks clear to me :slight_smile:

# Is there a better/faster way?

i do not go for faster, but maybe simpler to the eye,

c=[]
a.each_index{|i| c << a[i]+b[i]}

kind regards -botp

harp:~ > cat a.rb
# gem install narray
require 'narray'

a = NArray.to_na [1,1,1,1]
b = NArray.to_na [2,2,2,2]

p a + b

harp:~ > ruby a.rb
NArray.int(4):
[ 3, 3, 3, 3 ]

-a

···

On Wed, 5 Jul 2006, Nasir Khan wrote:

If I have two arrays a=[1,1,1,1] and b=[2,2,2,2] and I want to add each
element up, something like -

c = a plus b => [3,3,3,3]

One way I could think up is -

irb(main):010:0> (0...a.length).collect{|k| a[k]+b[k]}
=> [3, 3, 3, 3]

Is there a better/faster way?

Thanks
Nasir

--
suffering increases your inner strength. also, the wishing for suffering
makes the suffering disappear.
- h.h. the 14th dali lama

Nasir Khan wrote:

If I have two arrays a=[1,1,1,1] and b=[2,2,2,2] and I want to add each
element up, something like -

c = a plus b => [3,3,3,3]

One way I could think up is -

irb(main):010:0> (0...a.length).collect{|k| a[k]+b[k]}
=> [3, 3, 3, 3]

Is there a better/faster way?

I'm not sure about faster, but this is probably more readable:

irb(main):001:0> require "matrix"
=> true
irb(main):002:0> Vector[1,1,1,1] + Vector[2,2,2,2]
=> Vector[3, 3, 3, 3]

Daniel

better would be c = (0...a.length).inject() { |x, n| x + [ a[n] + b[n] ] }

The example you showed breaks the functional aspect of inject.
(it is doing double assignment)

I just learned to avoid this myself....

···

On 7/4/06, Matthew Smillie <M.B.Smillie@sms.ed.ac.uk> wrote:

# using inject is fun. Let's do it again.
c = (0...a.length).inject() { |x, n| x << a[n] + b[n] }

I like this:

  a.zip(b).map{|(x,y)| x + y}

Not as fast as the Array.new(size) version, but the block doesn't need
to know which arrays it's operating on.

BTW, does anyone know why zip with a block returns nil? It would be
handy to have it map.

Regards,
Sean

···

On 7/5/06, Peña, Botp <botp@delmonte-phil.com> wrote:

i do not go for faster, but maybe simpler to the eye,

c=
a.each_index{|i| c << a[i]+b[i]}

kind regards -botp

Sean wrote:
# Botp wrote:
# > i do not go for faster, but maybe simpler to the eye,
# >
# > c=[]
# > a.each_index{|i| c << a[i]+b[i]}
# >
# > kind regards -botp
# >
# I like this:

···

#
# a.zip(b).map{|(x,y)| x + y}

Yes, Sean, zip w map is cool, really. (i think you can lose the parens, too).
Zip allows any number of arrays. so now we have to keep track of how many arrays we are zipping.

irb(main):002:0> a=[1,2,3]
=> [1, 2, 3]
irb(main):003:0> b=[4,5,6]
=> [4, 5, 6]
irb(main):004:0> c=[7,8,9]
=> [7, 8, 9]
irb(main):005:0> a.zip(b).map{|x,y| x + y }
=> [5, 7, 9]
irb(main):007:0> a.zip(b,c).map{|x,y,z| x + y + z }
=> [12, 15, 18]

Maybe, we just create a Array#sum to simplify and scale further, like so,

irb(main):016:0> a.zip(b).map{|e| e.sum}
=> [5, 7, 9]
irb(main):017:0> a.zip(b,c).map{|e| e.sum}
=> [12, 15, 18]

no change in inside block. ruby is cool.

# Not as fast as the Array.new(size) version, but the block doesn't need
# to know which arrays it's operating on.

and no length/size pre-det, too :slight_smile:

# BTW, does anyone know why zip with a block returns nil? It would be
# handy to have it map.

maybe, ruby is trying to tell us beware. if we screw our block, we might get bloated run-away arrays. maybe we should map to be sure...

kind regards -botp

# Regards,
# Sean

#zip with a block acts as an #each style iterator:

% cat zip_example.rb
a = [1, 2, 3]
b = [4, 5, 6]

a.zip(b) do |a_elem, b_elem|
   p [a_elem, b_elem]
end

% ruby zip_example.rb
[1, 4]
[2, 5]
[3, 6]

You either get the #map style functionality or the #each style functionality. #each is more general, e.g.:

a.to_enum(:zip, b).map { |x, y| ... }

so it makes sense to me that it's nto a map with a block.

···

On Jul 4, 2006, at 11:13 PM, Sean O'Halpin wrote:

BTW, does anyone know why zip with a block returns nil? It would be
handy to have it map.

ugh this is doubly slow. #inject, an extra literal array and an Array#+ for
each pair, making it O(n**2)...

If you absolutely want to feel smart for using #inject,

a = [1,2,3,4]
b = [1,1,2,2]
c = a.inject([,0]){|(arr, i),e| [arr << e+b[i], i + 1]}.first
c # => [2, 3, 5, 6]

... but it's still silly. Either use the Array initializer, manual aref in a
a.size.times block, or look into narray.

···

On Wed, Jul 05, 2006 at 11:36:31AM +0900, Gregory Brown wrote:

On 7/4/06, Matthew Smillie <M.B.Smillie@sms.ed.ac.uk> wrote:

># using inject is fun. Let's do it again.
>c = (0...a.length).inject() { |x, n| x << a[n] + b[n] }

better would be c = (0...a.length).inject() { |x, n| x + [ a[n] + b[n] ] }

--
Mauricio Fernandez - http://eigenclass.org - singular Ruby

Sean wrote:
# Botp wrote:
# > i do not go for faster, but maybe simpler to the eye,
# >
# > c=
# > a.each_index{|i| c << a[i]+b[i]}
# >
# > kind regards -botp
# >
# I like this:
#
# a.zip(b).map{|(x,y)| x + y}

Yes, Sean, zip w map is cool, really. (i think you can lose the parens, too).

Hmm.. wonder what I was doing there?

Zip allows any number of arrays. so now we have to keep track of how many arrays we are zipping

.
Good point.

[snip zip examples]

Maybe, we just create a Array#sum to simplify and scale further, like so,

# here's one way to do it:
module Enumerable
  def sum(initial = 0)
    inject(initial) { |acc, el| acc + el }
  end
end

irb(main):016:0> a.zip(b).map{|e| e.sum}
=> [5, 7, 9]
irb(main):017:0> a.zip(b,c).map{|e| e.sum}
=> [12, 15, 18]

no change in inside block. ruby is cool.

Certainly is :slight_smile:

For example:

module Enumerable
  def zip_map(*args, &block)
    zip(*args).map( &block )
  end
end
# and with nobu's definition of Symbol#to_proc
class Symbol
  def to_proc
    Proc.new{|*args| args.shift.__send__(self, *args)}
  end
end

# we could do this:
a=[1,1,1,1]
b=[2,2,2,2]
c=[3,3,3,3]
p a.zip_map(b, c, &:sum)
#=> [6, 6, 6, 6]

This isn't particularly fast though!

Regards,
Sean

···

On 7/5/06, Peña, Botp <botp@delmonte-phil.com> wrote:

It's not quite the same - each returns the receiver not nil. I was
wondering what the rationale of zip returning nil was. It would be
more consistent to return the receiver and perhaps handier to act like
map. No big deal though.

Regards,
Sean

···

On 7/5/06, Logan Capaldo <logancapaldo@gmail.com> wrote:

On Jul 4, 2006, at 11:13 PM, Sean O'Halpin wrote:

> BTW, does anyone know why zip with a block returns nil? It would be
> handy to have it map.

#zip with a block acts as an #each style iterator:

# Here is another variation (using Symbol#to_proc)
class Symbol
  def to_proc
    Proc.new { |obj, *args| obj.send(self, *args) }
  end
end

class Array
  def c2(a) # parallel collect
    (0 .. size - 1).collect {|i| yield self[i], a[i]}
  end
  def c2op(a,opb)
    c2(a,&opb)
  end
end

# Use with a following block
p [4,3,2,1].c2([2,2,2,2]) {|a,b| a+b}
# => [6, 5, 4, 3]

# or just send the desired operator as a symbol
a=[4,3,2,1]
b=[2,2,2,2]
p a.c2op(b, :+)
# => [6, 5, 4, 3]

# ... or even re-define addition, subtraction etc.
class Array
  def +(b)
    self.c2op(b, :+)
  end
  def -(b)
    self.c2op(b, :slight_smile:
  end
end

a=[4,3,2,1]
b=[2,2,2,2]
p a + b
# => [6, 5, 4, 3]
p a - b
# => [2, 1, 0, -1]

-- Mike Berrow

···

--
Posted via http://www.ruby-forum.com/.

Well there's no rational in array.c's comments so I have no idea.

(the code is just return Qnil; )

···

On Jul 5, 2006, at 1:18 AM, Sean O'Halpin wrote:

On 7/5/06, Logan Capaldo <logancapaldo@gmail.com> wrote:

On Jul 4, 2006, at 11:13 PM, Sean O'Halpin wrote:

> BTW, does anyone know why zip with a block returns nil? It would be
> handy to have it map.

#zip with a block acts as an #each style iterator:

It's not quite the same - each returns the receiver not nil. I was
wondering what the rationale of zip returning nil was. It would be
more consistent to return the receiver and perhaps handier to act like
map. No big deal though.

Regards,
Sean

The word is rationale. It isn't rational for people to use "rational"
when they mean "rationale." Better, use "explanation."

Not trying to pick on you, Logan, but there've been a *lot* of cases
in the last two weeks where people have used "rational" when they
meant "rationale" or "explanation." Different parts of speech.

-austin

···

On 7/5/06, Logan Capaldo <logancapaldo@gmail.com> wrote:

Well there's no rational in array.c's comments so I have no idea.

--
Austin Ziegler * halostatue@gmail.com * http://www.halostatue.ca/
               * austin@halostatue.ca * You are in a maze of twisty little passages, all alike. // halo • statue
               * austin@zieglers.ca