[#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:38405] Re: Enumerable#gather_each

From: Tanaka Akira <akr@...>
Date: 2009-05-10 01:08:47 UTC
List: ruby-dev #38405
In article <86r5yy2nrg.knu@iDaemons.org>,
  "Akinori MUSHA" <knu@iDaemons.org> writes:

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

どのようにわかりにくかったでしょうか?

> どうでしょうか。

まず、gather_each に比べてコードが長くなってよろしくありません。

たとえば、最初に出したパラグラフの例は以下のように長くなってしまいます。

buffer:
  open("lib/scanf.rb") {|f|
    f.buffer {|e, b|
      s = e == "\n"
      b.flush if b.status != nil && b.status != s
      b.status = s
      b << e
    }.each {|lines| pp lines }
  }

gather_each:
  arg = lambda {|l| l == "\n" }
  open("lib/scanf.rb") {|f|
    f.gather_each(arg) {|lines| pp lines }
  }

また、gather_each は、ある要素に対する処理は、その要素とそれ
が属するまとまりのことだけを考えて書けばいいようにデザインし
てあるのですが、buffer では直前のまとまりを flush する必要が
あって、直前のまとまりについても考えなければいけません。
つまり、buffer のほうが考えることが多くなってよろしくありま
せん。

たとえば、ChangeLog をエントリ単位に処理するには、行頭が空白
じゃなかったら flush すればいいというわけで以下のようにすれ
ばいいと考えるかもしれませんが、実はそれだけだと先頭に [] が
表示されるというバグが発生してしまいます。

  open("ChangeLog") {|f|
    f.buffer {|e, b|
      b.flush if /\A\S/ =~ e
      b << e
    }.each {|lines| pp lines }
  }

ちゃんとやるには条件を /\A\S/ =~ e && !b.empty? にしないとい
けません。

こういう、直前がなんだったか、というのを考えなくていいという
のが gather_each の利点です。

あと、Buffer には status という機能がありますが、状態をどう
しても管理したいならレキシカルスコープの変数でいいんじゃない
でしょうか。たとえば、パラグラフの例は以下のように書いてもいい
わけです。

  open("lib/scanf.rb") {|f|
    status = nil
    f.buffer {|e, b|
      s = e == "\n"
      b.flush if status != nil && status != s
      status = s
      b << e
    }.each {|lines| pp lines }
  }

そして、gather_each でも同様に状態は扱えて、ChangeLog のエン
トリは以下のように処理できます。(gather を使ってみました)

  open("ChangeLog") {|f|
    i = 0
    f.gather {|l| i += 1 if /\A\S/ =~ l; i }.each {|lines| pp lines }
  }

なお、私としてはこれを勧めているわけではなくて、ユーザがそう
いう状態遷移を考えなくて済むというのが良いと思っています。
つまり、専用のメソッを作るほうが、状態を考えなくていいので良
いと思います。

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

gather_each でも、要素を結果から除去する指定は出来てもいいか
な、という気はします。そのための値を分類結果に定義しておけば
可能で、nil をその意味にするか、あるいは :delete あたりにす
るか、なにがいいかな。

buffer はいろんなことができるようにするという意図が感じられ
ますが、現実的な用途の想定として、どんなものが考えられますか?

私は、gather_each とあともうひとつ ChangeLog みたいなものを
処理するものがあると、かなりの範囲の用途を扱えるのではないか、
と考えています。その推測が正しければ専用のメソッドのほうが便
利でしょう。
-- 
[田中 哲][たなか あきら][Tanaka Akira]

In This Thread