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)