[ruby-list:50847] Re: Stringの派生オブジェクトにsubを取った時のインスタンス変数のコピー

From: "Masa Sakano" <imagine@...>
Date: 2019-11-21 18:03:06 UTC
List: ruby-list #50847
坂野正明です。

コメントです。
String#sub 
において、元のオブジェクトのインスタンスを全てコピーする、という仕様は
あり得るとは思います。しかし、プログラミング言語として、そうであるべき必然はないと思います。

「コピー」するならば、具体的にどのようにコピーするか、という問題もありましょう
(単にassignment, dup, clone?)。
そして何より、一般論としては、メソッドの結果として内容が変わる、ということは、
「インスタンス変数を含めてオブジェクトの内容が変化する」ということですから、
仮にインスタンス変数を「コピー」するならば、どれをコピーしてどれをしないかあるいは変更するか、
という問題が一般論としては発生しそうです。例えば、String#length 
を保存するような
インスタンス変数が仮にあったとしたならば、それは 
String#sub の返り値では一般に変化しているべき
ということになりましょう。

そう考えれば、インスタンス変数は独立、という現在の 
Ruby の仕様は私は自然だと思うところです。
self を返すようなメソッドは別として(selfですから)。

正木さんご提案の方法は、私もしばしば使います。他にそうされている人がいると知ってちょっと安心しました。

坂野正明

On 21 Nov 2019, at 17:50, Takefumi URA wrote:

> 浦です。
> 正木さん、お返事ありがとうございます。
>
> 一応subが新たなインスタンスを作ることは理解していたつもりです。
> subが派生クラスのインスタンスを作ってくれるならインスタンス変数も
> ケアしてほしかったなという思い込みでした。
>
> なるほどsubをオーバーライドすればよかったですね。
> …ただ非破壊系のメソッドを全部オーバーライドするのはちょっと骨かなあという気がします。
> # ごめんなさいこの辺質問が足りませんでしたね
>
>> 2019/11/22 2:32、MASAKI Haruka <yek@reasonset.net>のメール:
>>
>> 正木です。
>>
>> String#subは自身を返すわけではありませんから、
>> 戻り値となっているオブジェクトはsubを呼び出したオブジェクトとは別物ですので、
>> インスタンス変数は共有されません。
>>
>> サブクラスではなく、特異クラスを使った場合でも違うオブジェクトなので、
>> 特異クラスは持っていない状態になります。
>>
>> #!ruby
>> str = "Hello"
>>
>> class <<str
>>  @foo = "foo"
>>  def foo
>>    @foo
>>  end
>> end
>>
>> str.foo # foo
>> str2 = str.sub("foo", "bar")
>> str2.foo # error
>>
>> varの値を継承したいなら次のようにするのはいかがでしょう。
>>
>> class NString < String
>>  #...
>>
>>  def var=(val)
>>    @var = val
>>  end
>>
>>  def sub
>>    new_nstr = super
>>    new_nstr.var = @val
>>    new_nstr
>>  end
>> end
>>
>>
>> On Fri, 22 Nov 2019 01:55:42 +0900
>> Takefumi URA <ura.takefumi@gmail.com> wrote:
>>
>>> 浦といいます。
>>>
>>> Stringを派生させたクラスのオブジェクトにインスタンス変数を設定しました。
>>> このオブジェクトにsubを適用してできたオブジェクトにはそのインスタンス
>>> 変数がコピーされていませんでした。以下のような感じです。
>>>
>>>
>>> $ cat test-string.rb
>>> class NString < String
>>>  def initialize(s)
>>>    @var = s.hash # 
>>> ハッシュ値を取ってるのは特に意味はない
>>>    super s
>>>  end
>>>
>>>  def inspect
>>>    "#<NString:#{@var}:#{self}>"
>>>  end
>>> end
>>>
>>> ns = NString.new('abc')
>>> p ns                            # => #<NString:val:abc>
>>> p ns.sub(/a/, 'A')              # => #<NString::Abc>
>>>
>>> $ ruby -v test-string.rb
>>> ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-darwin18]
>>> #<NString:3775343761872790250:abc>
>>> test-string.rb:8: warning: instance variable @var not initialized
>>> #<NString::Abc>
>>>
>>> ご覧の通り @var 
>>> が引き継がれておらず初期化もなされていません。
>>> このあたり、インスタンス変数もケアしてくれる 
>>> (subでcloneなりdupなりやってる)
>>> のかなと思っていたので少し意外でした。
>>>
>>> Stringのソースを見てないのでなんともなんですけど、
>>> subを取って新たなオブジェクトが得られた時 @var 
>>> の値をコピーするにはどうしたらよいでしょうか?
>>>
>>> 別解としてsub!を使ってそもそもコピーする必要をなくすというのはあるでしょうけど
>>> (というかそれでとりあえず解決しました)、純粋に疑問に思ったので質問してみることにしました。
>>>

In This Thread