ようへいの日々精進XP

よかろうもん

2018 年 05 月 24 日(木)

ジョギング

日課

  • お休み

玉ねぎとそら豆が

  • 実家から玉ねぎとそら豆が送られてきた
  • 玉ねぎはベランダに干しておいた

奥さん

  • 久しぶりに体調が悪いということで寝込んでいた
  • 無理せず, 自分の体に正直に, 焦らずに

今日のるびぃ ~ REx - Ruby Examination にチャレンジ (9) ~

REx - Ruby Examination の問題を自分なりにアレンジした上で 1 〜 3 問くらいずつ解いていく. 正直言ってかなり難しい. 尚, irb に動作確認環境は以下の通り.

$ ruby --version
ruby 2.1.10p492 (2016-04-01 revision 54464) [x86_64-linux]
$ irb --version
irb 0.9.6(09/06/30)

refinements

以下のコードを実行するとどうなるか.

# file name: refinement_1.rb
class Cls1
  def method1(value)
    100 + value
  end
end

module Mod1
  refine Cls1 do
    def method1
      super 50
    end
  end
end

module Mod2
  refine Cls1 do
    def method1
      super 100
    end
  end
end

using Mod1
using Mod2

puts Cls1.new.method1

以下, 実行例.

$ ruby refinement_1.rb 
200

ということで, 200 が表示される.

なぜか, 以下, 解説より抜粋.

  • 同一メソッドに対して, refinements を用いて, 2 つのモジュールで再定義している
  • using を 2 行書いた場合, 1 つのメソッドで有効になる再定義は 1 つだけ, 最後に書いた using が優先される
  • 設問で有効になるのは using Mod2 となる為, super + 100 = 100 + 100 = 200 となる

そもそも, refinements について.

  • 再定義したメソッドの適用範囲を限定する機能
  • 再定義したメソッドは using メソッドを利用して利用する
  • using メソッドは, トップレベル, クラス構文, モジュール構文内で利用することが出来る
  • トップレベルで using を呼び出した場合, using が呼び出された箇所からファイルの最後までとなる

以下, refinements の例. (プロを目指す人のためのRuby入門 言語仕様からテスト駆動開発・デバッグ技法まで 「8.9 モジュールに関する高度な話題」より引用)

module StringShuffle
  refine String do
    def shuffle
      chars.shuffle.join
    end
  end
end
  • refinements を使う準備としてモジュールを作成する
  • モジュール内で refine メソッドを使って, refinements を適用するクラス (上記の例では String クラス) を指定し, そのブロック内に shuffle メソッドを定義する

refinements を有効にする為に, 以下のように using メソッドを利用することで, Foo クラス内でのみ shuffle メソッドが利用可能となる.

irb(main):001:0> module StringShuffle
irb(main):002:1>   refine String do
irb(main):003:2*     def shuffle
irb(main):004:3>       chars.shuffle.join
irb(main):005:3>     end
irb(main):006:2>   end
irb(main):007:1> end
=> #<refinement:String@StringShuffle>
# 通常は String#shuffle は存在していない
irb(main):008:0> 'kappa'.shuffle
NoMethodError: undefined method 'shuffle' for "kappa":String
irb(main):009:0> class Foo
irb(main):010:1>   using StringShuffle # using メソッドで StringShuffle を利用する (refinements を有効にする)
irb(main):011:1>   def initialize(name)
irb(main):012:2>     @name = name
irb(main):013:2>   end
irb(main):014:1>   def shuffle_name
irb(main):015:2>     @name.shuffle
irb(main):016:2>   end
irb(main):017:1> end                   # Foo クラスを抜けると refinements は無効となる
=> :shuffle_name
irb(main):019:0> user = Foo.new('kappa')
=> #<Foo:0x005563c35d7d80 @name="kappa">
irb(main):022:0> user.shuffle_name
=> "ppaak"

もう少し. サンプルコードをドキュメントより引用.

class C
  def foo
    puts "C#foo"
  end
end

module M
  refine C do
    def foo
      puts "C#foo in M"
    end
  end
end

以下, irb にて確認.

# refinements 無し
irb(main):014:0> x = C.new
=> #<C:0x0055b46a444cf0>
irb(main):015:0> x.foo
C#foo
=> nil
# refinements 有り, だけど irb だと意図したような結果にならない
irb(main):016:0> using M
=> main
irb(main):017:0> x = C.new
=> #<C:0x0055b46a42bc28>
irb(main):018:0> x.foo
C#foo
=> nil
# refinements 有り, 以下のように一行で書けば意図したような結果になる
irb(main):019:0> using M; x = C.new; x.foo
C#foo in M
=> nil

フムフム.