2015-02-23 17:47

Rubyでの破壊的メソッドの作成方法

破壊的メソッドを作成してみる

破壊的メソッドとは

破壊的メソッドとは、メソッドの実行によってレシーバ自身を変更するメソッドです。

Rubyにおいては、破壊的メソッドのメソッド名の最後に!が付けられています。

ひとつ例を挙げておきます。

example.rb
1     str = "rock"
2
3     str.gsub("r", "d")
4     puts str + " ← gsubメソッドの結果"
5
6     str.gsub!("r", "d")
7     puts str + " ← gsub!メソッドの結果"
terminal.app
$ ruby example.rb
rock ← gsubメソッドの結果
dock ← gsub!メソッドの結果

この例では3行目でgsubメソッドを、6行目でgsub!メソッドを実行しています。

どちらも実行結果を変数に代入することをせずに出力しています。

結果を見てみるとgsubメソッドを実行した場合はstr = "rock"のままです。

それに対してgsub!メソッドを実行した場合はstr = "dock"とgsub!メソッドのレシーバである変数strの値が変更されています。

このように、返り値を変数に代入して受け取るのではなくて、レシーバを直接書き換えてしまうのが破壊的メソッドです。

破壊的メソッドの作成

前置きが少し長くなりましたが、本題の破壊的メソッドの作成を行ってみましょう。

ここではサンプルとして、Stringクラスを拡張して、レシーバとなる文字列の文字数を当該レシーバとなる文字列の末尾に追加して変更するメソッドを作成してみます。

まず、陥りがちな間違いから。

sample.rb
1     class String
2       def add_length!
3         self = self + "_#{self.length}"
4       end
5     end
6
7     str = "ruby"
8     str.add_length!
9
10   puts str
terminal.app
$ ruby sample.rb
sample.rb:3: Can't change the value of self
        self = self + "_#{self.length}"
                 ^

実行結果にエラーメッセージが返ってきます。

エラーメッセージの内容を見る前に、コードを確認しておきましょう。

1~5行でStringクラスの拡張を定義しています。

その中の2~4行目において、メソッドの作成を定義していて、3行目でメソッドの実装を行っています。

破壊的なメソッドを作成するのでメソッド名の最後には慣例的に!を付けています。

3行目ではself(レシーバ。このサンプルでは変数str)と任意の文字列を結合したものを、selfに代入しています。

これで変数strが3行目の右辺の内容に書き換わりそうなものです。

では、エラーの内容を見てみましょう。

sample.rbの3行目でCan't change the value of selfというエラーメッセージが送られています。

selfの値が書き換えられない?

そうなんです、selfには値を代入することができないのです。

ではどうするか。

次に正しい書き方を示します。

sample.rb
                    :
3         self.replace(self.to_s + "_#{self.length}")
                    :

3行目だけを修正しています。

selfをレシーバにしてreplaceメソッドを実行しています。

引数は、selfを置き換えたいオブジェクトです。

replaceメソッドですが、説明しなくてもメソッド名で挙動が推測できると思います。

これはレシーバを引数の内容で置き換えるメソッドです。
(これには!が付かないのですね…)

つまり、修正後の3行目ではselfをself.to_s + "_#{self.length}"で置き換えています。

実行してみましょう。

terminal.app
$ ruby sample.rb
ruby_4

期待通りの結果が返ってきました。

破壊的メソッドを作成する際にはself =ではなくself.replace()を使いましょう。