メソッド探索, 定数探索
参考
上記のサイトに掲載されていた問題を解いてみた.
インスタンスメソッド探索
[1] pry(main)> module M [1] pry(main)* def method_missing(id, *args) [1] pry(main)* puts "M#method_missing()" [1] pry(main)* end [1] pry(main)* end => :method_missing [2] pry(main)> [3] pry(main)> class A [3] pry(main)* include M [3] pry(main)* def method_missing(id, *args) [3] pry(main)* puts "A#method_missing()" [3] pry(main)* end [3] pry(main)* end => :method_missing [4] pry(main)> [5] pry(main)> class B < A [5] pry(main)* def method_x [5] pry(main)* puts "#{self.class.name}:method_x" [5] pry(main)* end [5] pry(main)* class << self [5] pry(main)* def method_missing(id, *args) [5] pry(main)* puts "B`method_missing()" [5] pry(main)* end [5] pry(main)* end [5] pry(main)* end => :method_missing [6] pry(main)> obj = B.new => #<B:0x005621b96edf58> # メソッドが見つかった [7] pry(main)> obj.method_x B:method_x => nil # メソッドが見つからず, 継承を辿って class A の method_missing() が呼ばれる [8] pry(main)> obj.method_y A#method_missing() => nil [9] pry(main)> B.ancestors => [B, A, M, Object, PP::ObjectMixin, Kernel, BasicObject]
class A を少し弄った場合...
[1] pry(main)> module M [1] pry(main)* def method_missing(id, *args) [1] pry(main)* puts "M#method_missing()" [1] pry(main)* end [1] pry(main)* end => :method_missing [2] pry(main)> [3] pry(main)> class A [3] pry(main)* include M [3] pry(main)* end => A [4] pry(main)> [5] pry(main)> class B < A [5] pry(main)* def method_x [5] pry(main)* puts "#{self.class.name}:method_x" [5] pry(main)* end [5] pry(main)* class << self [5] pry(main)* def method_missing(id, *args) [5] pry(main)* puts "B#method_missing()" [5] pry(main)* end [5] pry(main)* end [5] pry(main)* end => :method_missing [6] pry(main)> obj = B.new => #<B:0x00561ca95f1920> # class A で include M しているので, 最終的に Module M の method_missing() で捕捉される [7] pry(main)> obj.method_y M#method_missing() => nil # クラスメソッド B.method_p を呼んでいるけど, メソッドが定義されていないのでクラスメソッド内の method_missing() で捕捉される [8] pry(main)> B.method_p B#method_missing() => nil [9] pry(main)> B.ancestors => [B, A, M, Object, PP::ObjectMixin, Kernel, BasicObject]
class A で M モジュールを prepend した場合...
[1] pry(main)> module M [1] pry(main)* def method_missing(id, *args) [1] pry(main)* puts "M#method_missing()" [1] pry(main)* end [1] pry(main)* end => :method_missing [2] pry(main)> [3] pry(main)> class A [3] pry(main)* prepend M [3] pry(main)* def method_missing(id, *args) [3] pry(main)* puts "A#method_missing()" [3] pry(main)* end [3] pry(main)* end => :method_missing [4] pry(main)> [5] pry(main)> class B < A [5] pry(main)* def method_x [5] pry(main)* puts "#{self.class.name}:method_x" [5] pry(main)* end [5] pry(main)* class << self [5] pry(main)* def method_missing(id, *args) [5] pry(main)* puts "B`method_missing()" [5] pry(main)* end [5] pry(main)* end [5] pry(main)* end => :method_missing [6] pry(main)> B.new.method_x B:method_x => nil # class A で M モジュールを prepend しているので, A#method_missing() よりも先に M#method_missing() が呼ばれる [7] pry(main)> B.new.method_y M#method_missing() => nil
クラスメソッド探索
[1] pry(main)> class Module [1] pry(main)* def method_missing(id, *args) [1] pry(main)* puts "Module#method_missing()" [1] pry(main)* end [1] pry(main)* end => :method_missing [2] pry(main)> [3] pry(main)> class Class [3] pry(main)* def method_missing(id, *args) [3] pry(main)* puts "Class#method_missing()" [3] pry(main)* end [3] pry(main)* end => :method_missing [4] pry(main)> [5] pry(main)> module M [5] pry(main)* def method_missing(id, *args) [5] pry(main)* puts "M#method_missing()" [5] pry(main)* end [5] pry(main)* end => :method_missing [6] pry(main)> [7] pry(main)> class A [7] pry(main)* include M [7] pry(main)* def method_missing(id, *args) [7] pry(main)* puts "A#method_missing()" [7] pry(main)* end [7] pry(main)* end => :method_missing [8] pry(main)> [9] pry(main)> class B < A [9] pry(main)* def self.method_x [9] pry(main)* puts "#{self}.method_x" [9] pry(main)* end [9] pry(main)* def method_missing(id, *args) [9] pry(main)* puts "B#method_missing()" [9] pry(main)* end [9] pry(main)* end => :method_missing # Class B にメソッドを発見 [10] pry(main)> B.method_x B.method_x => nil # Class B にメソッドが無いので, 継承チェーンを辿り Class class の method_missing() で捕捉 [11] pry(main)> B.method_y Class#method_missing() => nil [12] pry(main)> B.ancestors => [B, A, M, Object, PP::ObjectMixin, Kernel, BasicObjec
- クラスメソッドが見つからない場合, Class クラス → Module クラスを探索する
定数探索
[1] pry(main)> class A [1] pry(main)* CNST_Y = "123" [1] pry(main)* def const_missing(id) [1] pry(main)* puts "A#const_missing()" [1] pry(main)* id = 4 [1] pry(main)* end [1] pry(main)* end => :const_missing [2] pry(main)> [3] pry(main)> class B < A [3] pry(main)* CNST_X = "456" [3] pry(main)* [3] pry(main)* def method01 [3] pry(main)* puts "CNST_X=#{CNST_X}" [3] pry(main)* puts "CNST_Y=#{CNST_Y}" [3] pry(main)* end [3] pry(main)* [3] pry(main)* def const_missing(id) [3] pry(main)* puts "B#const_missing()" [3] pry(main)* id = 5 [3] pry(main)* end [3] pry(main)* end => :const_missing [4] pry(main)> B.new.method01 CNST_X=456 CNST_Y=123 => nil
- 定数もインスタンスメソッド等と同様に継承チェーンを探索する
[1] pry(main)> class Module [1] pry(main)* def const_missing(id) [1] pry(main)* puts "Module#const_missing()" [1] pry(main)* id = 1 [1] pry(main)* end [1] pry(main)* end => :const_missing [2] pry(main)> [3] pry(main)> class Class [3] pry(main)* def const_missing(id) [3] pry(main)* puts "Class#const_missing()" [3] pry(main)* id = 2 [3] pry(main)* end [3] pry(main)* end => :const_missing [4] pry(main)> [5] pry(main)> class Object [5] pry(main)* def const_missing(id) [5] pry(main)* puts "Object#const_missing()" [5] pry(main)* id = 3 [5] pry(main)* end [5] pry(main)* end => :const_missing [6] pry(main)> [7] pry(main)> class A [7] pry(main)* def const_missing(id) [7] pry(main)* puts "A#const_missing()" [7] pry(main)* id = 4 [7] pry(main)* end [7] pry(main)* end => :const_missing [8] pry(main)> [9] pry(main)> class B < A [9] pry(main)* CNST_X = "123" [9] pry(main)* [9] pry(main)* def method01 [9] pry(main)* puts "CNST_X=#{CNST_X}" [9] pry(main)* puts "CNST_Y=#{CNST_Y}" [9] pry(main)* end [9] pry(main)* [9] pry(main)* def const_missing(id) [9] pry(main)* puts "B#const_missing()" [9] pry(main)* id = 5 [9] pry(main)* end [9] pry(main)* end => :const_missing [10] pry(main)> obj = B.new => #<B:0x00562cd0a1c3b0> [11] pry(main)> obj.method01 # 定数が見つかった CNST_X=123 # スーパークラスに定数が見つからなかったので, クラスメソッドの const_missing() を呼び出す Class#const_missing() CNST_Y=2 => nil
例外
[1] pry(main)> class Err1 < StandardError; end => nil [2] pry(main)> class Err2 < Err1; end => nil [3] pry(main)> begin [3] pry(main)* raise Err1 [3] pry(main)* rescue Err1 => ex [3] pry(main)* p ex.class [3] pry(main)* end Err1 => Err1 [4] pry(main)> begin [4] pry(main)* raise Err2 [4] pry(main)* rescue Err1 => ex [4] pry(main)* p ex.class [4] pry(main)* end Err2 => Err2
- raise で例外発生, 第一引数に例外クラス又はそのインスタンス, 第二引数にメッセージを指定
- rescue は例外クラスを指定しない場合, StandardError と, そのサブクラスが捕捉の対象となる
Time#strftime
[1] pry(main)> t = Time.now => 2018-01-23 23:55:16 +0900 [2] pry(main)> t.strftime("%m/%d/%Y") => "01/23/2018" [3] pry(main)> t.strftime("%m/%-d/%_6Y") => "01/23/ 2018" [4] pry(main)> t.strftime("at %I:%M%p") => "at 11:55PM" [5] pry(main)> t.strftime("at %I:%M%#p") => "at 11:55pm"
以下のようなフォーマット文字列が用意されている.(主なものを抜粋)
フォーマット | 説明 | 例 |
---|---|---|
%A | 曜日 | Wednesday |
%a | 曜日の略称 | Wed |
%B | 月の名称 | January |
%b | 月の略称 | Jan |
以下, 実行例.
[1] pry(main)> t = Time.now => 2018-01-24 00:00:50 +0900 [2] pry(main)> t.strftime("%A") => "Wednesday" [3] pry(main)> t.strftime("%a") => "Wed" [4] pry(main)> t.strftime("%B") => "January" [5] pry(main)> t.strftime("%b") => "Jan"
フォーマット | 説明 | 例 |
---|---|---|
%C | 世紀(2009年であれば 20) | 20 |
%c | 日付と時刻 | Wed Jan 24 00:00:50 2018 |
%D | 日付 (%m/%d/%y) | 01/24/18 |
%d | 日(01-31) | 24 |
%e | 日(01-31), 1 桁の場合には半角空白で埋める | 1 |
以下, 実行例.
[6] pry(main)> t.strftime("%C") => "20" [7] pry(main)> t.strftime("%c") => "Wed Jan 24 00:00:50 2018" [8] pry(main)> t.strftime("%D") => "01/24/18" [9] pry(main)> t.strftime("%d") => "24" [11] pry(main)> t = Time.at(0) => 1970-01-01 09:00:00 +0900 [12] pry(main)> t.strftime("%e") => " 1"
%e
は知らなかった...
フォーマット | 説明 | 例 |
---|---|---|
%F | %Y-%m-%d と同等 (ISO 8601の日付フォーマット) | 2018-01-24 |
%H | 24時間制の時(00-23) | 07 |
%h | %b と同等 | Jan |
%I | 12時間制の時(01-12) | 13 時の場合 01 となる |
以下, 実行例.
[13] pry(main)> t = Time.now => 2018-01-24 07:48:16 +0900 [14] pry(main)> t.strftime("%F") => "2018-01-24" [15] pry(main)> t.strftime("%H") => "07" [16] pry(main)> t.strftime("%h") => "Jan" [17] pry(main)> t.strftime("%I") => "07" [18] pry(main)> t = Time.new(2008, 6, 21, 13, 30, 0, "+09:00") => 2008-06-21 13:30:00 +0900 [19] pry(main)> t.strftime("%I") => "01"
%F
便利かも. そして, %I
がいつもよく解っていなかった.
フォーマット | 説明 | 例 |
---|---|---|
%j | 年中の通算日(001-366) | 024 |
%k | 24時間制の時。一桁の場合、半角空白で埋める ( 0..23) | 7 |
%L | ミリ秒 (000..999) | 132 |
%l | 12時間制の時。一桁の場合、半角空白で埋める ( 0..12) | 7 |
%M | 分(00-59) | 54 |
%m | 月を表す数字(01-12) | 01 |
以下, 実行例.
[20] pry(main)> t = Time.now => 2018-01-24 07:54:04 +0900 [21] pry(main)> t.strftime("%j") => "024" [22] pry(main)> t.strftime("%k") => " 7" [24] pry(main)> t.strftime("%L") => "132" [25] pry(main)> t.strftime("%l") => " 7" [26] pry(main)> t.strftime("%M") => "54" [27] pry(main)> t.strftime("%m") => "01"
%M
が分, %m
が月を表す. %L
はミリ秒, %l
(スモール L) は時間を表す.
フォーマット | 説明 | 例 |
---|---|---|
%n | 改行 (\n) | \n |
%N | 秒の小数点以下。桁の指定がない場合は9桁 (ナノ秒)、%6N: マイクロ秒 (6桁)、%3N: ミリ秒 (3桁) | 079192930 |
%P | 午前または午後(am,pm) | am |
%p | 午前または午後(AM,PM) | AM |
%R | 24時間制の時刻。%H:%M と同等 | 07:36 |
%r | 12時間制の時刻。%I:%M:%S %p と同等 | 07:36:43 AM |
以下, 実行例.
[1] pry(main)> t = Time.now() => 2018-01-25 07:36:43 +0900 [2] pry(main)> t.strftime("%n") => "\n" [3] pry(main)> t.strftime("%N") => "079192930" [4] pry(main)> t.strftime("%P") => "am" [5] pry(main)> t.strftime("%p") => "AM" [6] pry(main)> t.strftime("%R") => "07:36" [7] pry(main)> t.strftime("%r") => "07:36:43 AM"
フォーマット | 説明 | 例 |
---|---|---|
%S | 秒(00-60) (60はうるう秒) | 03 |
%s | 1970-01-01 00:00:00 UTC からの経過秒 | 1516833663 |
%T | 24時間制の時刻。%H:%M:%S と同等 | 07:41:03 |
%t | タブ文字 (\t) | 07\t41\t03 |
%U | 週を表す数。最初の日曜日が第1週の始まり(00-53) | 03 |
%u | 月曜日を1とした、曜日の数値表現 (1..7) | 4 |
以下, 実行例.
[1] pry(main)> t = Time.now() => 2018-01-25 07:41:03 +0900 [2] pry(main)> t.strftime("%S") => "03" [3] pry(main)> t.strftime("%s") => "1516833663" [4] pry(main)> t.strftime("%T") => "07:41:03" [6] pry(main)> t.strftime("%H%t%M%t%S") => "07\t41\t03" [7] pry(main)> t.strftime("%U") => "03" [8] pry(main)> t.strftime("%u") => "4"
フォーマット | 説明 | 例 |
---|---|---|
%v | VMS形式の日付 (%e-%b-%Y) | 25-JAN-2018 |
%V | ISO 8601形式の暦週 (01..53) | 04 |
%W | 週を表す数。最初の月曜日が第1週の始まり(00-53) | 04 |
%w | 曜日を表す数。日曜日が0(0-6) | 4 |
%X | 時刻 | 07:48:10 |
%x | 日付 | 01/25/18 |
%Y | 西暦を表す数 | 2018 |
%y | 西暦の下2桁(00-99) | 18 |
%Z | タイムゾーン | JST |
%z | タイムゾーン, UTCからのオフセット (例 +0900) | +0900 |
%:z | タイムゾーン, コロンが入ったUTCからのオフセット (例 +09:00) | +09:00 |
%::z | タイムゾーン,コロンが入った秒まで含むUTCからのオフセット (例 +09:00:00) | +09:00:00 |
%% | %自身 | % |
以下, 実行例.
[1] pry(main)> t = Time.now() => 2018-01-25 07:48:10 +0900 [2] pry(main)> t.strftime("%v") => "25-JAN-2018" [3] pry(main)> t.strftime("%V") => "04" [4] pry(main)> t.strftime("%W") => "04" [5] pry(main)> t.strftime("%w") => "4" [6] pry(main)> t.strftime("%X") => "07:48:10" [7] pry(main)> t.strftime("%x") => "01/25/18" [8] pry(main)> t.strftime("%Y") => "2018" [9] pry(main)> t.strftime("%y") => "18" [10] pry(main)> t.strftime("%Z") => "JST" [11] pry(main)> t.strftime("%z") => "+0900" [12] pry(main)> t.strftime("%:z") => "+09:00" [13] pry(main)> t.strftime("%::z") => "+09:00:00" [14] pry(main)> t.strftime("%%") => "%"
以上
写経とメモでした.