#to_sまたは#to_str?Rubyで型を明示的にキャストするか暗黙的に強制するか
型強制とは、オブジェクトの型をその値とともに別の型に変更することです。たとえば、#to_s
を使用して整数を文字列に変更します または、#to_i
を使用して整数に浮動小数点数を指定します 。おそらくあまり知られていない#to_str
および#to_int
一部のオブジェクトが実装するメソッドは一見同じですが、いくつかの違いがあります。
AppSignalアカデミーのこのエディションでは、タイプキャスティングのアクターについて簡単に触れながら、Rubyでタイプを明示的にキャストし、暗黙的に強制する方法について詳しく説明します。両方の方法の違いについて説明し、それらの使用方法について説明します。
まず、明示的なキャストヘルパーを使用して、Rubyで通常どのように値をさまざまな型に強制変換するかを見てみましょう。
明示的なキャストヘルパー
最も一般的なキャストヘルパーは#to_s
です 、#to_i
、#to_a
および#to_h
。これらは明示的なキャスト方法です。これらは、値をあるタイプから別のタイプに簡単に変換するのに役立ちます。
明示的なヘルパーには明確な約束があります。 #to_s
のときはいつでも オブジェクトで呼び出されると、常に オブジェクトが実際には文字列にうまく変換されない場合でも、文字列を返します。マイケル・キートンをバットマンとしてキャストするようなものです。コメディー俳優がその役に特に適していない場合でも、バットマンを獲得できます。
Rubyは、Ruby標準ライブラリのほぼすべての基本オブジェクトでこれらのヘルパーメソッドを提供します。
:foo.to_s # => "foo"
10.0.to_i # => 10
"10".to_i # => 10
これらのメソッド、特に#to_s
、Rubyのほとんどの基本タイプに実装されています。キャストはほとんどの場合値を返しますが、結果は期待したものとは異なる場合があります。
"foo10".to_i # => 0
[1, 2, 3].to_s # => "[1, 2, 3]"
{ :foo => :bar }.to_s # => "{:foo=>:bar}"
{ :foo => :bar }.to_a # => [[:foo, :bar]]
Object.to_s # => "Object"
Object.new.to_s # => "#<Object:0x00007f8e6d053a90>"
#to_s
を呼び出す 、#to_i
、#to_a
および#to_h
ヘルパーは、選択したタイプに任意の値を強制します。値に何が起こったかに関係なく、強制されたタイプの表現を返します。
暗黙の強制メソッド
キャスト先の型とは異なる値に対して型キャストメソッドを呼び出すと、エラーやデータの損失が発生する可能性があります。 Rubyは、オブジェクトが型のように動作する場合にのみ値を返す暗黙の強制メソッドも提供します。このようにして、値が目的のタイプのように機能することを確認できます。これらの暗黙の強制メソッドは#to_str
です。 、#to_int
、#to_ary
および#to_hash
。
暗黙の強制は、スポック以外の役割としてレナード・ニモイをキャストするようなものです。キャラクターがスポックに十分近い場合は機能しますが、そうでない場合は失敗します。 #to_str
ヘルパー試行 文字列に変換しますが、NoMethodError
が発生します オブジェクトがメソッドを実装しておらず、暗黙的に強制できない場合。
10.to_int # => 10
10.0.to_int # => 10
require "bigdecimal"
BigDecimal.new("10.0000123").to_int # => 10
# Unsuccessful coercions
"10".to_int # => NoMethodError
"foo10".to_int # => NoMethodError
[1, 2, 3].to_str # => NoMethodError
{ :foo => :bar }.to_str # => NoMethodError
{ :foo => :bar }.to_ary # => NoMethodError
Object.to_str # => NoMethodError
Object.new.to_str # => NoMethodError
Rubyは、要求された型に対して強制するものと強制しないものについて、もう少し厳密になっていることがわかります。強制が不可能な場合は、#to_*
メソッドはオブジェクトに実装されておらず、メソッドを呼び出すとNoMethodError
が発生します 。
暗黙の強制を使用する場合、例: #to_str
、元の型もStringのように機能する場合にのみ、Stringオブジェクトを返すように関数に要求します。このため、#to_str
Ruby標準ライブラリのStringにのみ実装されています。
Rubyが暗黙の強制を使用する方法
強制中に私たちが求めていることをより正確にする以外に、暗黙の強制は他に何に役立ちますか? Rubyは、かなりのシナリオで暗黙の強制自体を使用していることがわかりました。たとえば、オブジェクトを+
と組み合わせる場合 。
name = "world!"
"Hello " + name # => "Hello world!"
# Without #to_str
class Name
def initialize(name)
@name = name
end
end
"Hello " + Name.new("world!") # => TypeError: no implicit conversion of Name into String
ここでは、RubyがTypeError
を発生させているのがわかります。 Name
から暗黙の変換を行うことができないため String
に入力します 。
#to_str
を実装する場合 クラスでは、RubyはName
を強制する方法を知っています。 タイプ。
# With #to_str
class Name
def to_str
@name
end
end
"Hello " + Name.new("world!") # => "Hello world!"
配列と#to_ary
でも同じことが機能します 。
class Options
def initialize
@internal = []
end
def <<(value)
@internal << value
end
end
options = Options.new
options << :foo
[:some_prefix] + options # => TypeError: no implicit conversion of Options into Array
class Options
def to_ary
@internal
end
end
[:some_prefix] + options # => [:some_prefix, :foo]
しかし、#to_ary
より多くのシナリオで使用されます。これを使用して、配列を個別の変数に分解できます。
options = Options.new
options << :first
options << :second
options << :third
first, second, third = options
first # => :first
second # => :second
third # => :third
また、オブジェクトをブロックパラメータに変換します。
[options].each do |(first, second)|
first # => :first
second # => :second
end
#to_hash
など、暗黙の強制メソッドが使用されるシナリオは他にもあります。 **
を使用 。これにより、値が#to_hash
のハッシュに強制されます。 parse_options
に渡す前に メソッド。
class Options
def to_hash
# Create a hash from the Options Array
Hash[*@internal]
end
end
def parse_options(opts)
opts
end
options = Options.new
options << :key
options << :value
parse_options(**options) # => {:key=>:value}
強制タイプ
また、Rubyは、型が不明な型であり、正しい型を確実に取得したい場合に、より回復力のある強制メソッドを提供します。基本タイプごとに1つあります(String(...)
、Integer(...)
、Float(...)
、Array(...)
、Hash(...)
など)。
String(self) # => "main"
String(self.class) # => "Object"
String(123456) # => "123456"
String(nil) # => ""
Integer(123.999) # => 123
Integer("0x1b") # => 27
Integer(Time.new) # => 1204973019
Integer(nil) # => TypeError: can't convert nil into Integer
String(...)
メソッドは最初に#to_str
を呼び出そうとします 値に基づいて、それが失敗すると、#to_s
を呼び出します。 方法。すべてのオブジェクトが#to_str
を定義しているわけではありません メソッド、したがって、暗黙の強制(#to_str
)および明示的(#to_s
)メソッドをキャストすると、文字列変換が機能する可能性が高くなり、必要な値が得られます。最初に暗黙的な強制を呼び出すことにより、同じ値であるが強制されたタイプであり、"#<Object:0x00007f8e6d053a90>"
のような結果ではない結果が得られる可能性が高くなります。 。
class MyString
def initialize(value)
@value = value
end
def to_str
@value
end
end
s = MyString.new("hello world")
s.to_s # => "#<MyString:0x...>"
s.to_str # => "hello world"
String(s) # => "hello world"
強制型のように機能するオブジェクトに対してのみ、暗黙的なキャストメソッドを実装する必要があります。 #to_str
独自のStringクラス用。
最初に暗黙の強制を試みる以外に、String(...)
ヘルパーは、返されたタイプもチェックします。 #to_str
文字列以外でも、あらゆるタイプの値を返すことができる単なるメソッドです。要求されたタイプの値を確実に取得するためにString(...)
TypeError
を発生させます タイプが一致しない場合。
class MyString
def to_str
nil
end
end
s = MyString.new("hello world")
s.to_s # => "#<MyString:0x...>"
s.to_str # => nil
String(s) # => "#<MyString:0x...>"
ここでは、Rubyが#to_str
の結果を無視していることがわかります。 nil
を返したため 、これは文字列型ではありません。代わりに、#to_s
にフォールバックします 結果。
#to_s
の場合 nil
も返します したがって、正しいタイプではありません、String(...)
TypeError
が発生します 。
class MyString
def to_str
nil
end
def to_s
nil
end
end
s = MyString.new("hello world")
s.to_s # => nil
s.to_str # => nil
String(s) # => TypeError: can't convert MyString to String (MyString#to_s gives NilClass)
型強制を強制する場合は信頼性が高くなる可能性がありますが、キャストヘルパーメソッド(String(...)
、Integer(...)
など)は、指定された値に対してより多くのチェックを実行する必要があるため、通常は少し遅くなります。
結論
オブジェクトに適切なタイプのデータを処理していることを確認したい場合は、型強制が便利なプロセスです。この投稿では、#to_s
のような明示的なキャストヘルパーの知識を更新しました 、#to_i
、#to_a
および#to_h
。 #to_str
のような暗黙のヘルパーのインスタンスも調べました 、#to_int
、#to_ary
および#to_hash
便利で、Ruby自体がどのように使用するか。
この型強制の概要がお役に立てば幸いです。また、俳優の型キャストのアナロジーをどのように見つけたかをご覧ください。いつものように、あなたが私たちにカバーしてほしいトピックがあるかどうか私たちに知らせてください。ご質問やご意見がございましたら、お気軽に@AppSignalまでご連絡ください。
-
Rubyでの挿入ソートを理解する
注:これは、Rubyを使用したさまざまなソートアルゴリズムの実装を検討するシリーズのパート4です。パート1ではバブルソート、パート2では選択ソート、パート3ではマージソートについて説明しました。 データを並べ替えるためのさまざまな方法を引き続き検討するため、挿入並べ替えに目を向けます。挿入ソートが好きな理由はたくさんあります!まず、挿入ソートは安定です。 、これは、等しいキーを持つ要素の相対的な順序を変更しないことを意味します。 インプレースアルゴリズムでもあります 、は、並べ替えられた要素を格納するための新しい配列を作成しないことを意味します。最後に、挿入ソートは、すぐにわかるように、実
-
Ruby2.6の9つの新機能
Rubyの新しいバージョンには、新しい機能とパフォーマンスの改善が含まれています。 変更についていきますか? 見てみましょう! 無限の範囲 Ruby 2.5以前のバージョンは、すでに1つの形式の無限範囲をサポートしています( Float ::INFINITY を使用) )、しかしRuby2.6はこれを次のレベルに引き上げます。 新しい無限の範囲 次のようになります: (1..) これは、(1..10)のような終了値がないため、通常の範囲とは異なります。 。 使用例 : [a, b, c].zip(1..) # [[a, 1], [b, 2], [c, 3]] [1,2,3,