say, i have:
a = [1,2,3] and b = [4,5,6]
and i want to get another array, with c[ix] = a[ix] * b[ix].
(i want to get c = [4,10,18])
is there a builtin function in array.rb (that i have missed).
atm, i use a block to traverse thru every single element, but this still
makes one big or 2 small lines.
(e. g.: c =[]; a.each_with_index{ |ix| c[ix] = a[ix] * b[ix] } )
this should be way more elegant ;)
~ibotty
Not quite, but for parallel traversing made easier
a.zip(b).map {|i, j| i*j}
martin
a.zip(b).map { |x,y| x*y } # => [4, 10, 18]
I don't know if this is more elegant to you or not, but here is another way:
c = a.zip(b).map {|x| x[0] * x[1]}
Wes
Hey, this is just like what I just posted, except better! =)
(That's what I get for replying before reading the rest of the thread... ;)
Wes
you can use the Enumerable#zip and Enumerable#collect methods:
c = a.zip(b).collect{|a_elt, b_elt| a_elt*b_elt}
i dont see more elegant :)
Ciao
Denis
>> is there a builtin function in array.rb (that i have missed).
>
> Not quite, but for parallel traversing made easier
>
> a.zip(b).map {|i, j| i*j}
this is indeed much more elegant.
but because array * array is not defined, me thinks. one could implement
(this simple case) this way.
just my 2¢, though
~ibotty
Here's a handy generalisation of the method:
class Array
def mapwith(*args, &block)
if block_given?
zip(*args).collect {|i, j| yield i, j}
else
f = args.pop
zip(*args).collect {|i, j| i.send(f, j)}
end
end
end
p [1,2,3].mapwith([4,5,6]) {|i,j| i*j}
p [1,2,3].mapwith([4,5,6], :*)
martin
cheers..
Olivier.
> ibotty <m...@ibotty.net> wrote:
> >
> >> Not quite, but for parallel traversing made easier
> >>
> >> a.zip(b).map {|i, j| i*j}
> >
> > this is indeed much more elegant.
> >
> > but because array * array is not defined, me thinks. one could implement
> > (this simple case) this way.
In APL, * and other operators work like this, element-by-element.
> Here's a handy generalisation of the method:
>
> class Array
> def mapwith(*args, &block)
> if block_given?
> zip(*args).collect {|i, j| yield i, j}
> else
> f = args.pop
> zip(*args).collect {|i, j| i.send(f, j)}
> end
> end
> end
>
> p [1,2,3].mapwith([4,5,6]) {|i,j| i*j}
> p [1,2,3].mapwith([4,5,6], :*)
Should #mapwith work with 3 or more arrays like #zip does?
p [1,2,3].mapwith([4,5,6], [7,8,9]) {|i,j,k| i*j+k}
Allowing #zip to take a block has been discussed before. Here's yet
another implementation:
class Array
alias _zip zip
def zip(*args)
if block_given?
_zip(*args).map {|a| yield a}
else
_zip(*args)
end
end
end
p [1,2,3].zip([4,5,6], [7,8,9]) {|i,j,k| i*j+k}
Unfortunately, it doesn't allow taking a symbol to an operator (:*)
like #mapwith does.
But isn't that the wrong answer?
I thought that [1, 2, 3] * [4, 5, 6] would be:
[ [4, 5, 6],
[8, 10, 12],
[12, 15, 18]
] or some such (I might have reversed the arguments--and I don't think
it's commutative. [Admittedly, it's been a few decades since I did such
math.]). And if you meant the dot product rather than the cross
product, you would get a scalar.
I think this is what the original poster wanted. He doesn't
seem to be asking for a cross product OR a dot product as
far as I can tell, but just a nice idiomatic way of doing
a specific operation.
Hal
Right you are. I'll rework it in the a.m. - too sleepy right now :)
> Allowing #zip to take a block has been discussed before. Here's yet
> another implementation:
>
> class Array
> alias _zip zip
> def zip(*args)
> if block_given?
> _zip(*args).map {|a| yield a}
> else
> _zip(*args)
> end
> end
> end
>
> p [1,2,3].zip([4,5,6], [7,8,9]) {|i,j,k| i*j+k}
>
> Unfortunately, it doesn't allow taking a symbol to an operator (:*)
> like #mapwith does.
The symbol was my main motivation for writing it, actually - map {|x,y|
x*y} just felt redundant.
martin
>>> a.zip(b).map {|i, j| i*j}
>> But isn't that the wrong answer?
>> [...]
>> And if you meant the dot product rather than the cross
>> product, you would get a scalar.
> I think this is what the original poster wanted. He doesn't
> seem to be asking for a cross product OR a dot product as
> far as I can tell, but just a nice idiomatic way of doing
> a specific operation.
we do have a Vector (defined in matrix.rb (in 1.6 at least))
this Vector should define cross and dot product (well, cross product it
should not necessarily define, too much specific to three dimension
vectors, huh? ;)
so in short, i think this a product of two arrays should not be a cross or
dot product. never. use a vector instead, if you want to.
btw: this is not meant to be rude, but why put something in array, when it
belongs to something else.
~ibotty
class Array
def mapwith(*args, &block)
if block_given?
zip(*args).collect {|i| yield *i}
else
f = args.pop
zip(*args).collect {|i, *j| i.send(f, *j)}
end
end
end
p [1,2,3].mapwith([4,5,6], [7,8,9]) {|i,j,k| i*j+k}
p [[1],[2],[3]].mapwith([4,5,6], [7,8,9], :push)
martin
> ... (well, cross product it
> should not necessarily define, too much specific to three dimension
> vectors, huh? ;)
Perhaps we need a Tensor class :-).
If there was such one, maybe I'd finally have a hope of understanding those darned things!
Harry O.