[#38392] Enumerable#gather_each — Tanaka Akira <akr@...>

ときに、複数行をまとめて扱いたいことがあります。

47 messages 2009/05/09
[#38394] Re: Enumerable#gather_each — ujihisa <ujihisa@...> 2009/05/09

ujihisaと申します。

[#38400] Re: Enumerable#gather_each — Yukihiro Matsumoto <matz@...> 2009/05/09

まつもと ゆきひろです

[#38399] Re: Enumerable#gather_each — "Akinori MUSHA" <knu@...> 2009/05/09

At Sat, 9 May 2009 15:30:20 +0900,

[#38405] Re: Enumerable#gather_each — Tanaka Akira <akr@...> 2009/05/10

In article <86r5yy2nrg.knu@iDaemons.org>,

[#38417] Re: Enumerable#gather_each — "Akinori MUSHA" <knu@...> 2009/05/10

At Sun, 10 May 2009 10:08:47 +0900,

[#38524] [Bug #1503] -Kuをつけた時、/[#{s}]/n と Regexp.new("[#{s}]",nil,"n") で実行結果が異なる — sinnichi eguchi <redmine@...>

Bug #1503: -Kuをつけた時、/[#{s}]/n と Regexp.new("[#{s}]",nil,"n") で実行結果が異なる

8 messages 2009/05/22

[ruby-dev:38399] Re: Enumerable#gather_each

From: "Akinori MUSHA" <knu@...>
Date: 2009-05-09 17:23:41 UTC
List: ruby-dev #38399
At Sat, 9 May 2009 15:30:20 +0900,
Tanaka Akira wrote:
> というわけで、Enumerable#gather_each(arg) {|ary| ... } の提
> 案です。引数には Proc を与えて、これは各要素について呼ばれ、
> 結果として分類結果を返します。gather_each はその分類結果が等
> しい連続した要素を配列としてまとめて yield します。
>
> なお、分類結果が nil というのは特別扱いで、その要素は単独で
> まとまりとなることを示します。

 少し仕様がわかりにくいように思います。繰り返しというストリームの
バッファという発想から、次のようなインターフェースを考案しました。
どうでしょうか。

class Enumerator
  class Buffer
    # status: 自由なメモ用途に提供
    attr_accessor :status

    def initialize(yielder)
      @buffer = []
      @yielder = yielder
      @status = nil
    end

    # バッファに追加
    def push(item)
      @buffer.push(item)
    end
    alias << push

    # バッファの最後の要素を取り出す
    def pop
      @buffer.pop
    end

    # バッファをクリア
    def clear
      @buffer.clear
      self
    end

    # バッファが空かどうか
    def empty?
      @buffer.empty?
    end

    # バッファに溜まった要素数
    def size
      @buffer.size
    end

    # バッファの内容を yield してクリア
    def flush
      @yielder.yield(@buffer)
      @buffer = []
      nil
    end
  end
end

module Enumerable
  def buffer(&block)
    Enumerator.new { |yielder|
      buffer = Enumerator::Buffer.new(yielder)
      each { |*args|
        case args.size
        when 0
          block.call(buffer)
        when 1
          block.call(args.first, buffer)
        else
          block.call(args, buffer)
        end
      }
      buffer.flush unless buffer.empty?	    # ※終了時に自動flush
    }
  end
end

# 使用例:
#   要素をバッファに溜めていき、 nil が現れたら
#   そこまでのまとまりを yield する

p [1,2,3,nil,4,5,nil,6].buffer {|e, b|
  if e.nil?
    b.flush
  else
    b << e
  end
}.to_a

# 表示結果: [[1, 2, 3], [4, 5], [6]]

> なお、複数行をまとめる方法として使いそうなものは、まとめる先
> 頭要素を検出する方法を指定するとか、他にもいくつかあるように
> 思います。たとえば ChangeLog や mbox を扱うのには、先頭要素
> を指定するのがいいでしょう。そういうものはまた別のメソッドと
> して作るのがいいのではないかと思います。

 上記のインターフェースだと使用例のように区切りを渡さないことも
できますし、 yield を追加の前にするか後にするかも自由です。また、
status/status= を提供することで状態遷移を扱うことも支援できます。

 あとは、終了時のハンドラを定義すると上記の※印の代わりに実行
するようにするといいかなと思っています。

--
Akinori MUSHA / http://akinori.org/

In This Thread