ようへいの日々精進XP

よかろうもん

俺は Ruby について 1bit も理解していなかったので写経する 〜 memo 編 (2) 〜

メソッド探索, 定数探索

参考

上記のサイトに掲載されていた問題を解いてみた.

インスタンスメソッド探索

[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("%%")
=> "%"

以上

写経とメモでした.