- 定数のスコープ
- 再定義可能な演算子を再定義してみる
- YAML
- ブロックにおけるreturn,break,next
- Case式
- トップレベル
- 少数の加算時の誤差
- Classクラスからクラスを定義する
- 変数、メソッドが定義される場所
- 参考URL
定数のスコープ
# トップレベル定数 VAR = "main" module MyModule puts VAR + " main1" # => main main1 puts ::VAR + " main2" # => main main2 # モジュール内で定数定義 VAR = "MyModule" puts VAR + " module1" # => MyModule module1 自スコープの定数が優先 puts ::VAR + " main3" # => main main3トップレベル定数は上書きされていない end class MyClass puts VAR + " main1@MyClass" # => main main1@MyClass # クラス内定数 VAR = "MyClass" puts VAR + " MyClass1@MyClass" # => MyClass MyClass1@MyClass def my_method puts VAR + " MyClass3@my_method" puts MyClass::VAR + " MyClass4@my_method" # メソッド内定数 # VAR = "MyMethod" # => メソッド内で定義できない。 end include MyModule puts VAR + " MyClass2@MyClass" # => MyClass MyClass2@MyClass 自スコープの定数が優先 puts MyModule::VAR + " MyModule1@MyClass" # => MyModule MyModule1@MyClass end # メソッド内から定数を参照 mc = MyClass.new mc.my_method # => MyClass MyClass3@my_method # => MyClass MyClass4@my_method class MyChildClass < MyClass puts VAR + " MyClass1" # => MyClass MyClass1 # クラス(子)内の定数 VAR = "MyChildClass" puts VAR + " MyChildClass1" # => MyChildClass MyChildClass1 puts MyClass::VAR + " MyClass2" # => MyClass MyClass2 puts MyModule::VAR + " MyModule1" # => MyModule MyModule1 end
再定義可能な演算子を再定義してみる
Team クラスを作成して
+ : メンバーの追加
- : メンバーの削除
<< : チームまるごとメンバーに追加
というクラスを作成してみます。
class Team attr_reader :members def initialize(members = []) @members = members end # メンバーの追加 def +(member) @members << member end # メンバーの削除 def -(member) @members.delete(member) end # チームの追加 def <<(team) # teamにmembersメソッドが存在するかを確認 fail 'error' unless team.respond_to?(:members) @members += team.members end end team_a = Team.new(%w(tanaka sato suzuki)) print team_a.members, "\n" # => ["tanaka", "sato", "suzuki"] team_a + 'obokata' print team_a.members, "\n" # => ["tanaka", "sato", "suzuki", "obokata"] team_a - 'obokata' print team_a.members, "\n" # => ["tanaka", "sato", "suzuki"] team_b = Team.new(%w(matz larry guido)) team_a << team_b print team_a.members, "\n" # => ["tanaka", "sato", "suzuki", "matz", "larry", "guido"]
YAML
YAML形式のファイルの書き方
配列
array.yaml
- aaa - - b1 - - b3.1 - ccc
ハッシュ
hash.yaml
A: aaa B: B1: b1 B2: b2 C: c2
YAMLファイルの読み込み
require 'yaml' p YAML.load_file('./array.yaml') #=> ["aaa", ["b1", ["b2.1", "b2.2"]], "ccc"] p YAML.load_file('./hash.yaml') #=> {"A"=>"aaa", "B"=>{"B1"=>"b1", "B2"=>"b2"}, "C"=>"c2"}
ブロックにおけるreturn,break,next
ブロック内での動作
- return: ブロック、メソッドを抜ける
- break: ループを抜ける
- next: 次のループを移る
動作確認
def block_return puts '----return-----' 10.times do |i| return if i == 3 puts i end puts 'end' end def block_break puts '----break-----' 10.times do |i| break if i == 3 puts i end puts 'end' end def block_next puts '----next-----' 10.times do |i| next if i == 3 puts i end puts 'end' end block_return block_break block_next #----return----- #0 #1 #2 #----break----- #0 #1 #2 #end #----next----- #0 #1 #2 #4 #5 #6 #7 #8 #9 #end
Proc内部でのreturn,break,nextの動作は以下に記載
Procクラスをirbで確かめる - 気軽に楽しくプログラムと遊ぶ
Case式
# 正規表現の場合 case name when /Andy|Bob/ p name else p "who are you?" end # 範囲 case age when 0..12 p 'You are a child.' when 13..19 p 'You are a teen-ager.' else p 'You are an adult.' end
トップレベル
概要
- selfはObjectオブジェクト(irbではmainオブジェクト)
- main.classはObjectを返却
- トップレベルで定義したメソッドはObjectクラスのprivateメソッドとして定義される
self #=> main self.class #=> Object # すべてのクラスから呼べるレシーバーなしの関数を定義できる def my_method puts 'my_method' end my_method #=> my_method # privateメソッドとして定義されている (・の部分は省略した) self.private_methods(false) #=> ["public",・・・・・・・・・・ "my_method"] # Objectはすべてのクラスで継承されているので、my_methodを呼び出せる class C my_method end #=> my_method 呼び出せた!
参考URL:Rubyのオブジェクトは生物なんかじゃない、トップレベルこそが生物なんだ!
少数の加算時の誤差
浮動小数点数1/5や1/3は2進数で正確に表せないため、誤差が生じる。
上記の数を2進数で表すには途中で切り捨てて表現するため、その分が誤差になる。
a = 0.1 + 0.2 b = 0.3 p a == b #=> false
BigDecimalクラスを用いて、計算精度を上げると10進数の小数点で計算を行うため、丸め誤差がなくなり、計算精度が上がる。
require 'bigdecimal' a = BigDecimal("0.1") + BigDecimal("0.2") b = BigDecimal("0.3") p a == b #=> true
計算精度が上がったので==で比較できる誤算になった。
浮動小数点数を用いる限り、丸め誤差、桁落ちの問題はなくならない。
Classクラスからクラスを定義する
String、ArrayなどのクラスはClassクラスのオブジェクトにすぎない。
Classクラスよりクラスを定義する方法を確認する。
class Animal def initialize(a) @a = a end def method1() @a end end Dog = Class.new(Animal) do # Animalがスーパークラス def initialize(a, b) @b = b super(a) end def method2(c) @a + @b + c end end dog = Dog.new("hello", " world") dog.method2(" !!") #=> "hello world !!"
変数、メソッドが定義される場所
class Foo @@var = 1 def self.class_method @@var end def instance_method @var = 1 end end # インスタンスオブジェクトを参照 foo = Foo.new foo.instance_method foo.instance_variables # => [:@var] # クラスを参照 Foo.class_variables # => [:@@var] Foo.instance_methods(false) # => [:instance_method] # Fooクラスにはclass_methodsメソッドが存在しない。クラスメソッドは特異クラスに定義される Foo.class_methods(false) # => NoMethodError: undefined method `class_methods' for Foo:Class # 特異クラスのメソッドを参照するにはクラスの特異メソッドを確認 Foo.singleton_methods(false) #=> [:class_method]
クラスメソッドが特異クラスに定義される理由
Classクラスにメソッドを定義すると、すべてのクラスに適用されてしまう。
class Foo end class Class def c_method1 puts "c_method1" end end Foo.c_method1 #=> c_method1
特定クラスに適用するために特異クラスに定義している。
特異クラスへのメソッド定義は特定オブジェクト(特定クラス)への定義となる。
クラスが保持するもの
- 継承関係
- メソッドの可視性
クラスがメソッドの可視性を保持しているため、サブクラスで可視性の変更が可能。
参考URL
Ruby | 再定義できる演算子 で組み込み演算子風のメソッドを定義する #ruby - Qiita
Ruby - ブロックについてのあれこれ - Qiita
Ruby の case 文 - Qiita