Rubyで変数がBoolean型かどうかチェックしたい。
かなりレアケースな気がするけれど、この前参加した勉強会で要望があったのでトライしてみた。
RubyのTrueとFalse
Rubyには is_bool? という関数はない
true.is_bool? # => undefined method `is_bool?' for true:TrueClass (NoMethodError)
エラーになります。
is_a?(クラス名)
というメソッドを使うとオブジェクトが引数のクラスのインスタンスかどうか判定できるので、これを利用する。
true
はTrueClass
、false
はFalseClass
のインスタンス。
Boolean
やBoolClass
ってクラスは無いっぽい。
val = true p val.is_a?(FalseClass) || val.is_a?(TrueClass) # => true val = false p val.is_a?(FalseClass) || val.is_a?(TrueClass) # => true val = nil p val.is_a?(FalseClass) || val.is_a?(TrueClass) # => false
チェックできるけど記述が長いので何回もチェックが必要だとめんどくさそう...
is_a? を使わない方法を考えてみる
値を!!
でtrue/falseに変換して比較したらいけそうな気がしました。
def is_bool(v) !!v === v end p is_bool true # => true p is_bool false # => true p is_bool nil # => false p is_bool 1 # => false p is_bool 0 # => false p is_bool "" # => false p is_bool true.to_s # => false p is_bool false.to_s # => false
なんとなく上手くいっている気もしますが、Rubyの===
演算子は型チェックをしているのではなく、左辺のオブジェクトによって判定の挙動が異なるらしいので本当にこれでOKなのか少し自信がありません。
ついでに関数化してみたのですが、is_boolって関数名で引数取る感じが何となくイケてない気がします...
他の言語を使っていると、ついつい「===演算子は==演算子よりも更に厳密な同値判定演算子でしょ?」なんて思ってしまいますが、ところがどっこい、Rubyの場合はむしろ===演算子のほうが柔軟な場合もあり得るのです。
Object#===の説明を見てみるとわかりますが、基本的にこの===演算子は、サブクラスで再定義されていない限り、単にObject#==を呼び出すだけなんですね。
TrueClass
, FalseClass
では#===
が定義されていないようだったので、Object#===
と同じなのではないかと思います。
ってコトは !!v==v
でも同じことっぽい!?
Object
obj === other → true or false
Case Equality – For class Object, effectively the same as calling #==, but typically overridden by descendants to provide meaningful semantics in case statements.
出展: https://ruby-doc.org/core-2.2.2/Object.html#method-i-3D-3D-3D
Rubyは定義済みのクラスにメソッドとかを追加できるっぽい
手持ちの初めてのRubyという本を見ていると、オブジェクトとクラスの章にRubyは「定義済みのクラスに対して、いつでも定義を追加することができます。」とありました。
Objectクラスにis_bool?
というメソッドを追加することも可能っぽかったので試してみました。
class Object def is_bool? !!self === self end end p true.is_bool? # => true p false.is_bool? # => true p nil.is_bool? # => false p 1.is_bool? # => false p 0.is_bool? # => false p "".is_bool? # => false p true.to_s.is_bool? # => false p false.to_s.is_bool? # => false
一応 Object.is_bool?
でBool値かどうか判定できるっぽものが出来ました。
メソッド内の判定をself.is_a?(FalseClass) || self.is_a?(TrueClass)
にした方が判定としては確実かもしれません。
ただ、個人的に大元になるObjectクラスにメソッドを追加するのは正直どーなの?という感覚があるので良し悪しが判断できません。
こうするとCoolだよとかRubyっぽいよ。ってのがあれば教えてください。
[参考]
- is_a?, kind_of? (Object) - Rubyリファレンス
- オブジェクトのクラスを動的に判定するには (instanceof) | 型とクラス | プログラミング言語の比較 | hydroculのメモ
- RubyでBoolClassではなくTrueClass/FalseClassな理由を理解する
- Rubyのcase式と===演算子について - しばそんノート
- Rubyのtrueとfalse - かもメモ
- 作者: Yugui
- 出版社/メーカー: オライリージャパン
- 発売日: 2008/06/26
- メディア: 大型本
- 購入: 27人 クリック: 644回
- この商品を含むブログ (251件) を見る