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

From: "H.Hiro" <main@...>
Date: 2019-11-26 08:02:58 UTC
List: ruby-list #50851
H.Hiroです。

単純に興味本位でやってみました。
単純な例でしか動作を確認できていないので、様々な場合に対応できるかわからないところはありますが。
方針としては、Stringを継承するのではなく、Stringをメンバ変数として持たせてmethod_missingで対応するというものになっています。

標記のコードはここにも置いてあります。 https://wandbox.org/permlink/lZ8Dom09iq5yXoia

class NString < BasicObject
  # method_missingで「存在しないメソッドはとりあえずStringに投げてみる」ということをするため、
  # ObjectではなくBasicObjectのほうがおそらく都合がよい
  # https://docs.ruby-lang.org/ja/latest/class/BasicObject.html
  def initialize(s, v = s.hash)
    @str = ::String.new(s) # @str が String 型になることを保証したい
    @var = v
  end

  def inspect
    "#<NString:#{@var}:#{@str}>"
  end

  def ==(other)
    @str == String.new(other)
  end

  def method_missing(name, *args)
    ret = @str.__send__(name, *args)
    if ret.instance_of?(::String)
      # 文字列が返ってきた場合に限り、 @var を引き継いで新たなインスタンスを作る
      ::NString.new(ret, @var)
    else
      ret
    end
  end

  def respond_to_missing?(name, include_private)
    @str.respond_to_missing?(name, include_private)
  end
end

ns = NString.new('abc')
p ns.length # Stringが持つメソッドのうち文字列以外を返すものについては、それをそのまま返す
p ns
p ns.sub(/a/, 'A')
p ns
p ns.sub!(/a/, 'B')
p ns # 上の sub! で @str を書き換えたので、ここでも書き換え後の値が表示される

2019年11月22日(金) 4:49 Takefumi URA <ura.takefumi@gmail.com>:
>
> 浦です。
>
> 正木さん、コメントありがとうございます。
>
> おっしゃるとおり嬉しくない例が圧倒的に多いと思います。
> この辺の挙動を変えろと言っているわけではありません。
> 変えるのもきっと無理でしょう。
> ただそれでもどうにか引っ掛けてなんとかする方法はないのかなと思ったのでした。
>
> おっしゃるとおりinitializeを上書きするのも
> 一応やってみましたが結果は芳しくありませんでした。
> initialize_copyというメソッドを見つけていじってみましたがこちらも
> 引っかかってはくれませんでした。
> これ以上はStringの実装読むしかないなと思いつつなにか方法ないかなと、
> 今の所必要なメソッドをすべて上書きする方法しかなさそうですね。
>
> イメージとしてはsubなど非破壊系のメソッドは元のStringオブジェクトから
> 新たなStringオブジェクトを作り出すわけで、
> (ここから妄想)そのときに非破壊系で共通して新旧オブジェクトを渡してくれる
> フックを定義できれば細工が楽になるけど、そういうのはないのかなと思ったのです。
> これもなんかなさそうな感じですね。
>
>
> > 2019/11/22 3:12、MASAKI Haruka <yek@reasonset.net>のメール:
> >
> > 正木です。
> >
> > インスタンス変数はそもそもがインスタンスに固有のものですから、
> > 新しいオブジェクトを返すのにインスタンス変数をコピーするというのは本筋ではないように思います。
> > クラスによっては他のオブジェクトと共有するとまずいインスタンス変数も普通にあるでしょうし、
> > 嬉しくないケースのほうが圧倒的に多いでしょう。
> >
> > というよりも、インスタンス変数のような固有情報を保って欲しいのであれば、
> > それはそのオブジェクトでなければいけないということである、ではないでしょうか。
> >
> > 新規オブジェクトを返すときに共通して呼ばれるのはinitializeですが、
> > Stringが新しいStringオブジェクトを返すときのnewメソッドの引数は文字列なので、
> > initializeでインスタンス変数をコピーすることはできません。
> > 呼び出し元のオブジェクトを知るチャンスがありませんので。
> >
> > そもそもStringにはない挙動を求めているということですから、
> > やるとすればサブクラス側に書くしかありませんね。
> > であれば、「スーパークラスとは違う振る舞いをしてほしいメソッド」をすべて示すことになります。
> >
> >
> > On Fri, 22 Nov 2019 02:50:56 +0900
> > Takefumi URA <ura.takefumi@gmail.com> wrote:
> >
> >> 浦です。
> >> 正木さん、お返事ありがとうございます。
> >>
> >> 一応subが新たなインスタンスを作ることは理解していたつもりです。
> >> subが派生クラスのインスタンスを作ってくれるならインスタンス変数も
> >> ケアしてほしかったなという思い込みでした。
> >>
> >> なるほどsubをオーバーライドすればよかったですね。
> >> …ただ非破壊系のメソッドを全部オーバーライドするのはちょっと骨かなあという気がします。
> >> # ごめんなさいこの辺質問が足りませんでしたね
> >>
> >> [...]
> >> [...]
> >>
>


-- 
H.Hiro / Maraigue
http://hhiro.net/about/
main@hhiro.net

In This Thread

Prev Next