Ruby には Fixnum, Float, Array, String など多くの組み込みクラスがあります。ところが驚いたことにその組み込みクラスの演算子をユーザが再定義することができるのです。この機能を利用して Array クラスの + 演算子と * 演算子を再定義(オーバーライド)して、irb をベクトル電卓に変身させてみましょう。
まず、irbをベクトル電卓に変身させる次のスクリプトを vector.rb という名前で作成します。
class Array
def isvector
if self.type == Array and self[0].type != Array
true
else
false
end
end
def ismatrix
if self.type == Array and self[0].type == Array
true
else
false
end
end
def +(other)
if self.isvector
temp = other.dup
self.collect {|x| x + temp.shift }
elsif self.ismatrix
temp = other.dup
self.collect do |row|
temp_row = temp.shift
row.collect {|x| x + temp_row.shift}
end
end
end
def -(other)
if self.isvector
temp = other.dup
self.collect {|x| x - temp.shift }
elsif self.ismatrix
temp = other.dup
self.collect do |row|
temp_row = temp.shift
row.collect {|x| x - temp_row.shift}
end
end
end
def *(other)
if other.type != Array
if self.isvector
self.collect {|x| x * other }
elsif self.ismatrix
self.collect do |raw|
raw.collect {|x| x * other }
end
end
elsif other.isvector
if self.isvector
result = 0.0
self.each_index {|i| result += self[i] * other[i]}
result
elsif self.ismatrix
result = []
self.each do |row|
sum = 0.0
row.each_index {|i| sum += row[i] * other[i]}
result.push( sum )
end
result
end
elsif other.ismatrix
m = self.size
p = self[0].size
n = other.size
result = []
for i in 0...m
row = []
for j in 0...n
row.push(0)
end
result.push(row)
end
for i in 0...m
for j in 0...n
sum = 0.0
for k in 0...p
sum += self[i][k] * other[k][j]
end
result[i][j] = sum
end
end
result
end
end
end
ちょっと長いスクリプトですが、Array クラスに isvector と ismatrix の二つのメソッドを追加し、それを利用して、+ 演算子、- 演算子、* 演算子で配列を利用したベクトル演算ができるように再定義を行っているだけです。Array オブジェクトである配列をベクトルのオブジェクトとして使い、配列の配列を行列のオブジェクトとして使っています。isvector ではオブジェクト a のタイプが Array 型で、a[0] のタイプが Array 型ではないとき a がベクトルであると判断して true を返します。また a.type も a[0].type も Array 型の時には a が行列であると判断して ismatrix の戻り値が true になります。
vector.rb の使い方は次のようになります。まずコンソールから irb と入力して irb を起動した後、require "vector.rb" を irb のプロンプトから実行します。これで、準備完了です。この操作で、Array クラスの演算子 + と * がベクトル演算の演算子に再定義されてしまうので、次のように irb をベクトル電卓として使うことができます。
$ irb irb(main):001:0> require "vector.rb" true irb(main):002:0> a = [1, 2] [1, 2] irb(main):003:0> a + a [2, 4] irb(main):004:0> a * 2 [2, 4] irb(main):005:0> a * a 5.0 irb(main):006:0> b = [[1, 0], [0, 1]] [[1, 0], [0, 1]] irb(main):007:0> b * a [1.0, 2.0] irb(main):008:0> c = [[1, 2], [3, 4]] [[1, 2], [3, 4]] irb(main):009:0> b * c [[1.0, 2.0], [3.0, 4.0]] irb(main):010:0> ( b * c ) * ( a * 2 ) [10.0, 22.0]
最後の行の例でも分かるように括弧を使った計算もちゃんとやってくれます。他のスクリプト言語であればパーサーから設計しないといけないところです。このようなヤドカリのような芸当をも簡単にやれてしまうのが Ruby という言語の不思議な所です。
Ruby のもう一つの面白いところはクラスを定義する文の class 文が実行可能な文であると言うことです。したがって、スクリプト実行中にも動的にクラスの性質の変更ができてしまいます。例えば上のベクトル電卓の場合ですが、irb を起動しただけだと次のように配列 a と 配列 b の + 演算子の結果は配列の結合になってしまいます。
$ irb irb(main):001:0> a = [1, 2] [1, 2] irb(main):002:0> b = [3, 4] [3, 4] irb(main):003:0> a + b [1, 2, 3, 4]
ところがその次に require 文で + 演算子と * 演算子の再定義を実行すると、a + b が今度はベクトルの和を返すようになります。
irb(main):004:0> require "vector.rb" true irb(main):005:0> a + b [4, 6]
このようにスクリプトを走らせたまま、クラスの仕様を変更することができるので、プログラムの開発を柔軟に行うことができます。(2001.11.1)