14

How do I perform vector addition in Ruby so that

[100, 100] + [2, 3] 

yields

[102, 103] 

(instead of joining two arrays)?

Or it can be another operator too, such as

[100, 100] @ [2, 3] 

or

[100, 100] & [2, 3]
Sinan Ünür
  • 116,958
  • 15
  • 196
  • 339
nonopolarity
  • 146,324
  • 131
  • 460
  • 740
  • If this were lisp, the solution would be to use map: (map + '(100 100) '(2 3)) => (102 103). Maybe Ruby has a similar function? – Kyle Cronin Jun 17 '09 at 20:16

6 Answers6

37

See the Vector class:

require "matrix"

x = Vector[100, 100]
y = Vector[2, 3]
print x + y

E:\Home> ruby t.rb
Vector[102, 103]

See vectorops for additional operations on vectors:

… the following operations work like expected

  v1 = Vector[1,1,1,0,0,0]
  v2 = Vector[1,1,1,1,1,1]

  v1[0..3]
  # -> Vector[1,1,1]

  v1 += v2
  # -> v1 == Vector[2,2,2,1,1,1]

  v1[0..3] += v2[0..3]
  # -> v1 == Vector[2,2,2,0,0,0]

  v1 + 2
  # -> Vector[3,3,3,1,1,1]

See also vectorops.

Sinan Ünür
  • 116,958
  • 15
  • 196
  • 339
19

Array#zip:

$ irb
irb(main):001:0> [100,100].zip([2,3]).map { |e| e.first + e.last }
=> [102, 103]

Shorter:

irb(main):002:0> [100,100].zip([2,3]).map { |x,y| x + y }
=> [102, 103]

Generalized to >2 dimensions with #inject:

irb(main):003:0> [100,100,100].zip([2,3,4]).map { |z| z.inject(&:+) }
=> [102, 103, 104]
Martin Carpenter
  • 5,893
  • 1
  • 28
  • 32
3

Or if you want arbitrary dimension behavior of that variety (like mathematical vector addition)

 class Vector < Array
   def +(other)
     case other
     when Array
       raise "Incorrect Dimensions" unless self.size == other.size
       other = other.dup
       self.class.new(map{|i| i + other.shift})
     else
       super
     end
   end
 end

class Array
  def to_vector
    Vector.new(self)
  end
end 

[100,100].to_vector + [2,3] #=> [102,103]

The lack of a lisp style map is quite obnoxious.

Ben Hughes
  • 14,075
  • 1
  • 41
  • 34
3

When in Rome..monkeypatch.

module Enumerable
  def sum
    inject &:+
  end

  def vector_add(*others)
    zip(*others).collect &:sum
  end
end

Then you can do a.vector_add(b) and it works. I believe this requires Ruby 1.8.7, or an extension that adds Symbol.to_proc. You can also add an arbitrary number of vectors this way.

stephenjudkins
  • 638
  • 6
  • 5
2

Just as a sidenote, if you (like me) felt unsatisfied with the operations that are provided by the default Vector class from ruby, consider giving my gem https://github.com/psorowka/vectorops a look, which adds some functionality I would expect from a proper Vector implementation.

Peter Sorowka
  • 1,036
  • 9
  • 20
1
module PixelAddition
  def +(other)
    zip(other).map {|num| num[0]+num[1]}
  end
end

Then you can either create an Array subclass that mixes in the module, or add the behavior to specific arrays like:

class <<an_array
  include PixelAddition
end
Chuck
  • 234,037
  • 30
  • 302
  • 389
  • 1
    Great use of modules, but having some instances of Array interpret the + operator in a different way than other instances really scares me. You might even get situations where (a + b) != (b + a). – molf Jun 17 '09 at 22:13