2017月11日20日
Ruby で配列を freeze する話
Ruby で配列を freeze したかったら以下。
irb(main):001:0> LIST = ['001', '002', '003'].freeze
=> ["001", "002", "003"]
ただし上のコードで freeze されるのは LIST
という配列オブジェクト自体で、配列の要素の値は freeze されない。
irb(main):002:0> LIST.frozen?
=> true
irb(main):003:0> LIST[0].frozen?
=> false
irb(main):004:0> LIST[0].class
=> String
配列オブジェクトの LIST
は freeze されているけど、その要素はやっぱり freeze されていない。Ruby の .freeze
メソッドは呼び出したオブジェクトを freeze する仕様なので、LIST
が .freeze
メソッドを呼んでも、その中の要素の String オブジェクトは影響を受けない。
なので中身の String オブジェクトも含めて freeze したければ以下の魔法にしておく。
irb(main):001:0> LIST = ['001', '002', '003'].map(&:freeze).freeze
=> ["001", "002", "003"]
irb(main):002:0> LIST[0].frozen?
=> true
irb(main):003:0> LIST.frozen?
=> true
魔法と書いたけど、単に配列を定義語にそいつを map して各要素に格納されているオブジェクト毎に freeze したあと、最後に配列自体を freeze しているだけ。
なので・・・。
上の挙動なので、以下のようなこともできる。
irb(main):001:0> LIST = ['001', '002', '003'].freeze
=> ["001", "002", "003"]
irb(main):002:0> LIST.frozen?
=> true
irb(main):003:0> LIST[1].freeze
=> "002"
irb(main):004:0> LIST[1].frozen?
=> true
irb(main):005:0> LIST[0].frozen?
=> false
配列自体は freeze、また、特定の要素も freeze。上の例だと LISTの2番目の文字列は freeze されているので変更できないけど、1番目と3番目は書き換え可能。だけど配列に新しい値を入れたり、配列オブジェクト自体に破壊的操作をすることはできない。