Ruby で block 内からレシーバのオブジェクトを参照したかった
意図としては以下のように block 内で呼び出したレシーバを参照したい。
[1, 2, 3].map { |it| it + self.size # block 内でレシーバのオブジェクトを参照したい } # => [4, 5, 6]
しかし、block 内のスコープでメソッドやオブジェクトを参照した場合、定義したスコープから参照されるのでこれでは意図した動作は行われない。
こういうことをしたい場合は一度変数に代入してから参照する必要がある。
list = [1, 2, 3] list.map { |it| it + list.size } # => [4, 5, 6]
しかし、こういうことをしたい場合にいちいち変数を定義するのはちょっともにょる。
#instance_eval を使う
代替え案その1。
#instance_eval
をかませることで、そのブロック内から自身を参照する事ができるようになります。
[1, 2, 3].instance_eval { map { |it| it + self.size } } # => [4, 5, 6]
ただし、その分スコープが深くなってしまうのがちょっと残念。
#tap を使う
某げっ歯類氏に相談してみたところ『#instance_eval
よりも #tap
の方が安全じゃない?』とのことで以下のような書き方を教えてもらった。
[1, 2, 3].tap { |myself| break myself.map { |it| it + myself.size } } # => [4, 5, 6]
#tap
の引数でレシーバのオブジェクトを受け取り、そこから自身を参照している。
ただし、#tap
の戻り値はレシーバのオブジェクトなので、任意の戻り値を返したい場合上記のように break
経由で返す必要がある。