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

On 7/5/06, Nasir K. [email protected] wrote:

Is there a better/faster way?

Thanks
Nasir

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

On Jul 5, 2006, at 1:16, Nasir K. wrote:

Is there a better/faster way?

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.

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

On 7/4/06, Matthew S. [email protected] 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]
] }

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

I just learned to avoid this myself…

On 7/5/06, Peña, Botp [email protected] 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}

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

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

On Wed, 5 Jul 2006, Nasir K. wrote:

Is there a better/faster way?

Thanks
Nasir

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 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:

% 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 7/5/06, Peña, Botp [email protected] wrote:

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

Nasir K. wrote:

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

On 7/5/06, Logan C. [email protected] 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

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 B.

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

On 7/4/06, Matthew S. [email protected] 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] ] }

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 Jul 5, 2006, at 1:18 AM, Sean O’Halpin wrote:

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

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

(the code is just return Qnil; )

On 7/5/06, Logan C. [email protected] wrote:

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

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