Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

#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までご連絡ください。


  1. Rubyでの挿入ソートを理解する

    注:これは、Rubyを使用したさまざまなソートアルゴリズムの実装を検討するシリーズのパート4です。パート1ではバブルソート、パート2では選択ソート、パート3ではマージソートについて説明しました。 データを並べ替えるためのさまざまな方法を引き続き検討するため、挿入並べ替えに目を向けます。挿入ソートが好きな理由はたくさんあります!まず、挿入ソートは安定です。 、これは、等しいキーを持つ要素の相対的な順序を変更しないことを意味します。 インプレースアルゴリズムでもあります 、は、並べ替えられた要素を格納するための新しい配列を作成しないことを意味します。最後に、挿入ソートは、すぐにわかるように、実

  2. 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,