Enumerable#gather_each

At Sun, 10 May 2009 08:53:31 +0900,
NARUSE, Yui wrote:

わたしもバッファを用意してーというのは考えたのですが、
田中さんのgather_eachで可能なことが、
gather_eachより複雑になってしまったらダメなんじゃないですかね。

 gather_each で可能なこと、つまり gather_each の結果がそのまま
ユーザが得たい最終結果となるケースは十分に多いのでしょうか。

 パラグラフのように同質のものの繰り返しならぴったりですが、そう
ã§ãªã„å ´åˆã¯ã€åˆ†å‰²ã•ã‚ŒãŸå€‹ã€…ãŒä½•ã§ã‚ã‚‹ã‹ã¨ã„ã†æƒ…å ±ãŒå¤±ã‚ã‚Œã¦ã„ã‚‹
ので処理の下流で再び検査しなければなりませんよね。また、捨てる
機能も必要なことが少なくないと思います。

 おそらくそれらは守備範囲外とするのでしょうが、範囲内としている
部分が十分に広いのか、そして実際の応用ニーズとの間に隙間はないか
という疑問を持っています。それがマッチするのなら、適切な名前で
è¿½åŠ ã™ã‚Œã°ã„ã„ã¨æ€ã„ã¾ã™ã€‚

田中さんのgather_eachでできることは、これ以上概念を増やさず
ほぼ同等の記述量でこなせつつ、自由度もあげられないといけないのかなと。

RubyなんでBufferみたいな概念を導入しても意外とすっきりするんですが、
そこで甘えちゃいけないんじゃないかと思うのですよ。

というか、これってバッファは別に提供して、

Enumerable版injectでやるべきな気も

 それは一つあるんですが、 yield を保留し、どこかのタイミングで
明示的に指定したものを yield するという機能が要求されるので、
yielder なりそれを包含する buffer なりを渡すというAPIは必要だと
思います。

 ときどき select と map を連結させることがありますが、それを
いっぺんに可能にする機能はほしいと思っています。たとえばこんな
感じです。

module Enumerable
  def with_yielder(&block)
    Enumerator.new { |yielder|
      each { |x|
        block.call(x, yielder)
      }
    }
  end
end

# 整数の書いてある行を抽出し、数列にする
p "abc\n123\n\n456\ndef\n".lines.with_yielder { |line, yielder|
  if m = /^([+\-]?\d+)$/.match(line)
    yielder << m[1].to_i
  end
}.to_a #=> [123, 456]

 これの前に with_object({}) でバッファやステータスを取るための
é ˜åŸŸã‚’ä»˜åŠ ã™ã‚Œã°ã€ãƒ¡ã‚½ãƒƒãƒ‰ãƒã‚§ãƒ¼ãƒ³ã§ãƒãƒƒãƒ•ã‚¡ã‚’å®Ÿè£…ã§ãã¾ã™ã­ã€‚

currentを読み捨てればいいだけなので全く同じコードで可能になります。
 これはよさそうです。

In article [email protected],
“Akinori MUSHA” [email protected] writes:

e$B!!$7$+$7!“e(B ChangeLog e$B$N$h$&$J>l9g$Oe(B gather_each e$B$bC1=c$5$r0];}e(B
e$B$G$-$:J#?t9T$K$J$k$H;W$$$^$9!#e(Bbuffer e$B$O!”$=$&$7$?>/$7$N0c$$$K$Oe(B
e$B>/$7$N0c$$$GBP1~$G$-$^$9!#e(B

e$B2<$K$"$ke(B ChangeLog e$B$NNc$O!"e(Bslice_before e$B$He(B gather
e$B$r;H$C$F=qe(B
e$B$/$H0J2<$N$h$&$J46$8$K$J$k$G$7$g$&$+!#e(B
(e$B$3$3$G;H$C$F$$$ke(B gather e$B$O!"e(B[ruby-dev:38418]
e$B$N;EMM$G!“e(Bnil
e$B$K$h$j$=$NMWAG$r=|5n$7$^$9!#$=$3$G$Oe(B slice_before e$B$b<BAu$7$Fe(B
e$B$”$j$^$9e(B)

open(“ChangeLog”) {|f|
f.slice_before {|l|
/\A\S/ =~ l
}.each {|h|
header = h.shift
h.gather {|l|
/\A\s*\z/ !~ l
}.each {|entry_lines|
next if /\A\s+*/ !~ entry_lines[0]
puts header, *entry_lines
puts “----”
}
}
}

gather e$B$de(B slice_before e$B$OF~NO$NJB$S$rJQ$($i$l$J$$$N$G!"$3$Ne(B
e$B$h$&$J=hM}$O$3$&$d$C$F2?CJ3,$+$KJ,$1$F$d$k$3$H$K$J$j$^$9$M!#e(B

e$B!!$O$$!#;d$NJ}$Oe(B gather_each e$B$,%+%P!<$G$-$kHO0O$N9-$5$K$D$$$Fe(B
e$B5?Ld$r;}$C$F$$$^$9$,!"FsBr$H$$$&OC$G$O$J$$$H;W$C$F$$$^$9!#e(B

e$B$=$&$G$9$M!#e(B

e$B!!>pJs$r<N$F$J$$$H$9$k$H!"%+%P!<HO0O$O$H$F$b69$/$J$k$H;W$$$^$9!#e(B
e$B$?$H$($P%3!<%IJR$NE&=P$NNc$O<BMQE*$J=hM}$H$7$F407k$7$J$$$N$G!"e(B
gather e$B$NM-MQ$J;HMQNc$H$7$F$O<e$$$N$G$O$J$$$G$7$g$&$+!#e(B

e$B$“$!!”<N$F$J$$$3$H$,=PMh$k!“$H=q$/$Y$-$G$7$?!#e(B
e$B$I$NCM$r<N$F$k0u$K$9$k$+$H$$$&OC$O$”$j$^$9$,!"<N$F$k5!G=$OIUe(B
e$B$1$h$&$H;W$C$F$$$^$9!#e(B

e$B%3!<%IJR$K$D$$$F$O!“%3!<%IJR$Ne(B syntax check e$B$r$d$k$N$G$”$l$Pe(B
e$BB>$O<N$F$F$$$$$G$7$g$&$7!“$^$?!“J8=q$NCf$N%3!<%IJR$re(B
(e$B?'$H$+e(B)
e$B%^!<%/%”%C%W$9$k$N$G$”$l$P!"B>$NItJ,$O;D$7$F$*$$$?$[$&$,$$$$e(B
e$B$G$7$g$&!#MQES<!Bh$G$9$M!#e(B

e$B!!$?$H$($P$G$9$,!"e(B RD e$B$de(B markdown e$B$N$h$&$JJ8=q$r=hM}$9$k>l9g$re(B
e$B9M$($F$$$^$7$?!#9M$($F$_$k$He(B gather e$B$N<iHwHO0O30$+$b$7$l$^$;$s$M!#e(B

RD e$B$Ne(B pre
e$B$O%$%s%G%s%H$,@hF,9T$N?<$50J>e$N$H$3$m$G$9$+$i$M$'!#e(B
buffer e$B$N$h$&$K>e$+$i=hM}$7$F$$$C$?$[$&$,<+A3$+$b!#e(B

e$B$b$7!“%$%s%G%s%H$r@hF,9T$h$j$b@u$/$G$-$k$h$&$J%U%)!<%^%C%H$Ge(B
e$B$”$l$P!“$”$H$+$i$d$C$?$[$&$,$$$$$G$7$g$&$M!#e(B

e$B$b$7!“$I$&$7$F$bJ,N`$NCM$,I,MW$@$H$$$&$3$H$G$”$l$P!“e(Byield e$B$9e(B
e$B$kG[Ns$K%$%s%9%?%s%9JQ?t$H$7$F$D$1$F$*$/e(B (e$BI,MW$J$i$=$l$r;2>He(B
e$B$9$k%”%/%;%5$be(B) e$B$N$K$O$d$V$5$+$G$O$"$j$^$;$s!#e(B

e$B!!$=$N>l9g$Ne(BAPIe$B$O$I$&$J$k$N$G$7$g$&!#e(Bgather e$BK\Mh$NMQES$K$O<YKb$Je(B
e$B%G!<%?$,$/$C$D$$$F$7$^$&$N$G!"JL%a%=%C%I$G$9$+$M!#e(B

ary.category e$B$H$+$G$$$$$s$8$c$J$$$G$7$g$&$+!#G[Ns$K%a%=%C%Ie(B
e$B$,$"$C$?$+$i$C$F<YKb$K$O$J$j$^$;$s$h$M!#e(B

enum.gather { … }.each {|ary| p ary.category }

e$B!!$`$7$m!“EDCf$5$s$N=P$5$l$?e(B2e$BLd$r6qBNNc$H$7$?$H$-$N;d$J$j$N2r$,e(B
Buffer e$B$J$s$G$9$h!#e(Bscanf.rbe$B$+$i$N%3!<%IJR$N@Z$j=P$7$H$$$&:G=i$Ne(B
e$BNc$O!”%3!<%IJR$G$J$$ItJ,$b4^$a$Fe(B pp e$B$9$k$H$$$&$N$,:G=L\E$G$Oe(B
e$B$J$$$G$9$h$M!#:o=|$9$k$H$$$&5!G=$,7g$1$F$$$k$N$GITMW$JItJ,$^$Ge(B
e$BF@$i$l$F$o$:$i$o$7$$!#=>$C$F<h<NA*Br$N5!G=$,I,MW$G$O$J$$$+!"$He(B
e$B;W$$$^$7$?!#e(B

gather e$B$N%V%m%C%/$N$J$K$+$NCM$K:o=|$N5!G=$r3d$jEv$F$h$&$H;We(B
e$B$$$^$9!#e(B

[ruby-dev:38418] e$B$N<BAu$@$H!"e(Bnil, false, :reject
e$B$,:o=|$K$J$Ce(B
e$B$F$^$9!#e(B

e$B!!<!$K%Q%i%0%i%U$4$H$K@Z$k$H$$$&Nc$Oe(B gather_each e$B$,$b$C$H$bC;$/e(B
e$B=q$1$kNc$G$7$?!#$7$+$7$J$,$i!“%X%C%@$r6h@Z$j$H$9$k9=B$$r07$&:]$Oe(B
e$BC;$/$J$/$J$k!#=>$C$FWs0UE*$J%?%$%_%s%0$Ge(B yield e$B$G$-$kI,MW$,$”$k!“e(B
e$B$”$k$$$O>uBV$N4IM}$r;Y1g$9$k$H$$$$$N$G$O$J$$$+!"$H;W$$$^$7$?!#e(B

e$B%X%C%@$r6h@Z$j$H$9$k>l9g$K$OJL%a%=%C%I$Ne(B slice_before e$B$,$$$$e(B
e$B$s$8$c$J$$$+$H;W$$$^$9!#e(B

e$B!!$3$l$i$r9g$o$;$?9M$($?$N$,e(B Buffere$B$G$9!#Ev$?$jA0$G$9$,J#;($J$3$He(B
e$B<+BN$rL;X$7$F$$$k$N$G$O$J$/$F!"e(B gather_each e$B$G$OIT==J,$H46$8!"e(B
e$B0lIt$N%1!<%9$r=|$1$P:GE,$J2r$H$O;W$($J$+$C$?$N$,BP0F$NF05!$G$9!#e(B

e$B$J$k$[$I!#e(B

Unix mbox e$B$N@Z$jJ,$1e(B:

slice_before (slice_by) e$B$K$h$k<BAu$Oe(B [ruby-dev:38407] e$B$K=q$-e(B
e$B$^$7$?$,!"e(Bbuf e$B$N07$$$,ITMW$J$V$se(B slice_before
e$B$N$[$&$,4J7i$Ge(B
e$B$9$M!#e(B

ChangeLog e$B$N3F%(%s%H%j$N@Z$j=P$7e(B:

e$B$3$l$O>e$K=q$-$^$7$?$,!"$b$C$HC;$/=q$1$^$9!#e(B

e$B4JC1$JEEBne(B:

e$B$J$s$Ge(B buffer e$B$r;H$&$s$G$9$+e(B?

STDIN.lines.buffer { |line, buf|
until line.empty?
line.sub!(/\A\s+/, ‘’)
line.sub!(/\A(\d*.?\d+)/) { buf << [:NUM, $1.to_f]; ‘’ }
line.sub!(/\A([+-*/])/) { buf << [:OP, $1.intern]; ‘’ }
end
buf.flush
}.tap { |expr|

e$B$3$l$C$F!“e(B1e$B9T$:$D%H!<%/%s$K$o$1$F!”$=$l$r=hM}$9$k$o$1$G$9$he(B
e$B$M!#e(B

STDIN.each_line {|line|
line e$B$r%H!<%/%s$K$o$1$Fe(B tokens e$B$KF~$l$ke(B
tokens e$B$r7W;;$7$FI=<(e(B
}

e$B$H$$$&$N$G$O$$$1$J$$$s$G$9$+e(B?

At Sun, 10 May 2009 22:08:55 +0900,
Tanaka A. wrote:

gather や slice_before は入力の並びを変えられないので、この
ような処理はこうやって何段階かに分けてやることになりますね。

 Buffer もそこについては悩みがあり、 pop は用意してみたものの
適切という確信はないですし、逆にもっとアクセス手段が必要という
ことなら、お仕着せの器の提供はやめるべきかもしれません。

あぁ、捨てないことが出来る、と書くべきでした。
どの値を捨てる印にするかという話はありますが、捨てる機能は付
けようと思っています。

コード片については、コード片の syntax check をやるのであれば
他は捨てていいでしょうし、また、文書の中のコード片を (色とか)
マークアップするのであれば、他の部分は残しておいたほうがいい
でしょう。用途次第ですね。

 そうですね。しかし、捨てる機能も含意すると名前が難しそうです。
もっとユースケースを集めてみないとわかりませんが、 gather は一定
条件でまとめる機能に特化して、捨てたりするのは汎用のメソッドを
設けて任せた方がいいのかもしれません。

簡単な電卓:

なんで buffer を使うんですか?

 例を小さくしたら必然性がなくなってしまいましたね。二重ループ
なのでブロックローカル変数で配列を用意しても同様でした。

At Sun, 10 May 2009 15:57:33 +0900,
Tanaka A. wrote:

In article [email protected],
“Akinori MUSHA” [email protected] writes:
(snip)

 最初の例というのが

arg = lambda {|l| /\A=~ l ? true : nil }
で読めなかったのですが、 l == “\n” でしたか。

あぁ、すいません。そこは /\A\s/ =~ l です。

パラグラフの例は最初のメール [ruby-dev:38392] のもうちょっと
下に出てきます。

 なるほど。空行は文脈依存と思ったのですが、 /\A\s/ が空行にも
マッチしてくれるので都合がいいわけですね。

これは

    prev, s.status = s.status, (e == "\n")
    b.flush if prev != b.status
    b << e

くらいで悪くはないと思います。b.status != nil のところは、上記の
「空なら flush しない」で手当てするとして。

それでも gather_each のほうがずっと短いですよね。

 はい、この例はまさに gather_each の守備範囲にぴったりです。

 しかし、 ChangeLog ã®ã‚ˆã†ãªå ´åˆã¯ gather_each も単純さを維持
できず複数行になると思います。buffer は、そうした少しの違いには
少しの違いで対応できます。

 やりすぎかもしれませんが、 status/status= を提供するのなら、
prev_status や status_changed? も用意するという手はあります。

私としては、状態変化をユーザが意識する必要はない用途は充分に
多いと考えています。

状態変化をそうやってサポートするのは、状態変化をユーザが意識
する必要がある用途には有用でしょう。しかし、まずそういう用途
が充分に多いのかという点について議論が必要ではないでしょうか。

 はい。私の方は gather_each がカバーできる範囲の広さについて
疑問を持っていますが、二択という話ではないと思っています。

 区切るだけなら確かに1行で済みますが、実際にはサンプルコード辺か
どうかを判定したり、前後の空行を除いたりと最終結果までの道のりは
長いので何とも言えません。要らない部分まで集めて(gather)いますが、
本当はもっと複雑な処理が必要なので buffer のようなものがあれば、
å–æ¨ã‚„åŠ å·¥ã«ã¤ã„ã¦ã‚‚å¼•ãå—ã‘ã‚‹ã“ã¨ãŒã§ãã‚‹ã¨æ€ã„ã¾ã—ãŸã€‚

後でやればいいんじゃないでしょうか。gather はまとめるだけで
æƒ…å ±ã‚’æ¨ã¦ã¾ã›ã‚“ã—ã€‚

ã€€æƒ…å ±ã‚’æ¨ã¦ãªã„ã¨ã™ã‚‹ã¨ã€ã‚«ãƒãƒ¼ç¯„å›²ã¯ã¨ã¦ã‚‚ç‹­ããªã‚‹ã¨æ€ã„ã¾ã™ã€‚
たとえばコード片の摘出の例は実用的な処理として完結しないので、
gather の有用な使用例としては弱いのではないでしょうか。

 上記の通り、実際に考えるべきことが後ろに残ると思うので、 gather
単体の提供する機能が中途半端に思えたのです。すなわち、インデント
レベル等、分類の基準として計算した値(ブロックの評価値)を捨てて
しまっていますが、この例でも、後段でまた必要になりそうですよね。

インデントの深さは、むしろ後から計算するほうがいいんじゃない
でしょうか。インデントされたブロックでは、複数の行のうち、もっ
とも浅いインデントが欲しそうですよね。

 たとえばですが、 RD ã‚„ markdown ã®ã‚ˆã†ãªæ–‡æ›¸ã‚’å‡¦ç†ã™ã‚‹å ´åˆã‚’
考えていました。考えてみると gather の守備範囲外かもしれませんね。

もし、どうしても分類の値が必要だということであれば、yield す
る配列にインスタンス変数としてつけておく (必要ならそれを参照
するアクセサも) のにはやぶさかではありません。

ã€€ãã®å ´åˆã®APIはどうなるのでしょう。gather 本来の用途には邪魔な
データがくっついてしまうので、別メソッドですかね。

ということであれば、 buffer を使って gather 等を実装するのは容易
なので、複数のメソッドを用意するのなら、実装を共有するためにも
buffer のような汎用のものを持つメリットがあるということになるの
ではないでしょうか。

実装してみましたが、そういうものがなくてもそれほど面倒ではあ
りませんでした。

 なるほど。実は map の Enumerator 版(配列を生成しない)を手元で
作っていたのですが、 Yielder があれば簡単なんですよね。

複雑なことができるからいろんな用途があるに違いない、という話
ではなく、具体的な例はありませんか?

 むしろ、田中さんの出された2問を具体例としたときの私なりの解が
Buffer なんですよ。scanf.rbからのコード片の切り出しという最初の
例は、コード片でない部分も含めて pp するというのが最終目的では
ãªã„ã§ã™ã‚ˆã­ã€‚å‰Šé™¤ã™ã‚‹ã¨ã„ã†æ©Ÿèƒ½ãŒæ¬ ã‘ã¦ã„ã‚‹ã®ã§ä¸è¦ãªéƒ¨åˆ†ã¾ã§
得られてわずらわしい。従って取捨選択の機能が必要ではないか、と
思いました。

 次にパラグラフごとに切るという例は gather_each がもっとも短く
æ›¸ã‘ã‚‹ä¾‹ã§ã—ãŸã€‚ã—ã‹ã—ãªãŒã‚‰ã€ãƒ˜ãƒƒãƒ€ã‚’åŒºåˆ‡ã‚Šã¨ã™ã‚‹æ§‹é€ ã‚’æ‰±ã†éš›ã¯
短くなくなる。従って恣意的なタイミングで yield できる必要がある、
あるいは状態の管理を支援するといいのではないか、と思いました。

 これらを合わせた考えたのが Bufferです。当たり前ですが複雑なこと
自体を目指しているのではなくて、 gather_each では不十分と感じ、
一部のケースを除けば最適な解とは思えなかったのが対案の動機です。

 いちおう、私からもいくつか具体例を挙げてみます。なお、空だったら
yield しないという改変を前提としています。Gist に上げました。

Unix mbox の切り分け:

file.lines.buffer { |line, buf|
buf.flush if /\AFrom / =~ line
buf << line
}

ChangeLog の各エントリの切り出し:

open(“ChangeLog”) { |f|
header = nil
f.lines.buffer { |line, buf|
case line
when /\A\S/
# é ­ãŒç©ºç™½ã§ãªã‘ã‚Œã°ãƒ˜ãƒƒãƒ€
header = line
when /\A\s*\z/
# 空行は区切り
buf.flush
when /\A\s+*/
# * で始まる行はエントリの始まり
buf.flush
if header
buf << header
buf << line
end
when /\A\s+\S/
# 空白で始まる行は、エントリが始まっていればエントリの継続行
buf << line unless buf.empty?
end
}.each { |header, *entry_lines|
puts header, *entry_lines
puts ‘----’
}
}

簡単な電卓:

STDIN.lines.buffer { |line, buf|
until line.empty?
line.sub!(/\A\s+/, ‘’)
line.sub!(/\A(\d*.?\d+)/) { buf << [:NUM, $1.to_f]; ‘’ }
line.sub!(/\A([+-/])/) { buf << [:OP, $1.intern]; ‘’ }
end
buf.flush
}.tap { |expr|
result = 0
calc = proc { |x, op, y|
case op
when :+
x + y
when :-
x - y
when :

x * y
when :confused:
x / y
end
}
expr.each { |tokens|
op = num = nil
tokens.each { |token|
type, data = token
case type
when :OP
if op && num
result = calc[result, op, num]
end
op = data
when :NUM
num = data
result = num if !op
end
}
if op && num
result = calc[result, op, num]
end
puts “> %g” % result
}
}

In article [email protected],
Yukihiro M. [email protected] writes:

|e$B0lEY$KFs$D$N<jB3$-$rEO$9$N$G$O$J$/!“0l$D$N%V%m%C%/$r<u$1<h$je(BEnumeratore$B$rJV$9e(Bgathere$B$H!”=>Mh$Ne(Beache$B$KJ,$1$?J}$,<+A3$K$J$k$N$G$O$J$$$G$7$g$&$+!#e(B

e$B;d$b$=$&;W$$$^$9!#$?$@!"e(BEnumerablee$B$+$i!V>r7o$rK~$?$98B$jO"B3e(B

e$B5$$,$D$$$?$s$G$9$,!"e(Bgather_each e$B$re(B gather e$B$H=>Mh$Ne(B
each e$B$KJ,e(B
e$B$1$k$H!"e(Bgather e$B$r8F$s$G:n$C$?e(B enumerator
e$B$KBP$7$F2?2s$be(B each
e$B$r8F$V$3$H$,$G$-$F$7$^$$$^$9$M!#e(B

e$B>uBV$r07$&$H$3$l$OLdBj$r@8$8$k$+$b$7$l$^$;$s!#e(B

e$B0J2<$Oe(B 3e$B$NG?tHVL$r=|5n$9$k$D$b$j$J%3!<%I$J$N$G$9$,!":G=i$Ne(B
to_a e$B$O0U?^$I$*$j$KF0$-$^$9$,!"e(B2e$B2sL$Ne(B to_a e$B$Oe(B (i
e$B$,e(B 0 e$B$8$ce(B
e$B$J$$$N$Ge(B) e$B7k2L$,0[$J$C$F$$$^$9!#e(B

i = 0
e = %w[a b c d e].gather { i += 1; i % 3 != 0 }
p e.to_a #=> [[“a”, “b”], [“d”, “e”]]
p e.to_a #=> [[“b”, “c”], [“e”]]

gather e$B$G$3$l$rHr$1$k$K$O!“0J2<$N$h$&$K$9$kI,MW$,$”$j$^$9!#e(B

e = Enumerator.new {|y|
i = 0
%w[a b c d e].gather { i += 1; i % 3 != 0 }.each {|a| y << a }
}
p e.to_a #=> [[“a”, “b”], [“d”, “e”]]
p e.to_a #=> [[“a”, “b”], [“d”, “e”]]

e$B$3$NE@$r9M$($k$H!“e(Bbuffer e$B$Ne(B status
e$B$K$O0UL#$,$”$C$?!"$H$$$&e(B
e$B$3$H$+$J$!!#e(B

In article [email protected],
Tanaka A. [email protected] writes:

gather e$B$be(B Yielder e$B$r;H$&7A$K$9$l$P!"e(Bgather_each e$B$r$J$/$9$3$He(B
e$B$O2DG=$@$H;W$$$^$9!#e(B

e$B$=$&$$$&7A$G:n$C$F$_$?$N$G$D$1$F$*$-$^$9!#e(B

e$B$U$H;W$$$D$$$?$N$G!“e(Bgather e$B$K$Oe(B chunk_by
e$B$H$$$&L>A0$b$D$1$F$”$j$^$9!#e(B

  • Enumerable#chunk_by
  • Enumerable#gather
  • Enumerable#slice_before

% svn diff --diff-cmd diff -x ‘-u -p’
Index: enum.c

— enum.c (revision 23381)
+++ enum.c (working copy)
@@ -1793,6 +1793,170 @@ enum_cycle(int argc, VALUE argv, VALUE
return Qnil; /
not reached */
}

+struct chunk_by_arg {

  • VALUE categorize;
  • VALUE prev_value;
  • VALUE prev_elts;
  • VALUE yielder;
    +};

+static VALUE
+chunk_by_ii(VALUE i, VALUE _argp, int argc, VALUE *argv)
+{

  • struct chunk_by_arg *argp = (struct chunk_by_arg *)_argp;
  • VALUE v;
  • VALUE singleton = ID2SYM(rb_intern(“_singleton”));
  • VALUE separator = ID2SYM(rb_intern(“_separator”));
  • ENUM_WANT_SVALUE();
  • v = rb_funcall(argp->categorize, rb_intern(“call”), 1, i);
  • if (v == singleton) {
  •    if (!NIL_P(argp->prev_value)) {
    
  •        rb_funcall(argp->yielder, rb_intern("<<"), 1, 
    

argp->prev_elts);

  •        argp->prev_value = argp->prev_elts = Qnil;
    
  •    }
    
  •    rb_funcall(argp->yielder, rb_intern("<<"), 1, rb_ary_new3(1, 
    

i));

  • }
  • else if (!RTEST(v) || v == separator) {
  •    if (!NIL_P(argp->prev_value)) {
    
  •        rb_funcall(argp->yielder, rb_intern("<<"), 1, 
    

argp->prev_elts);

  •        argp->prev_value = argp->prev_elts = Qnil;
    
  •    }
    
  • }
  • else if (SYMBOL_P(v) && rb_id2name(SYM2ID(v))[0] == ‘_’) {
  • rb_raise(rb_eRuntimeError, “symbol begins with an underscore is
    reserved”);
  • }
  • else {
  •    if (NIL_P(argp->prev_value)) {
    
  •        argp->prev_value = v;
    
  •        argp->prev_elts = rb_ary_new3(1, i);
    
  •    }
    
  •    else {
    
  •        if (rb_equal(argp->prev_value, v)) {
    
  •            rb_ary_push(argp->prev_elts, i);
    
  •        }
    
  •        else {
    
  •            rb_funcall(argp->yielder, rb_intern("<<"), 1, 
    

argp->prev_elts);

  •            argp->prev_value = v;
    
  •            argp->prev_elts = rb_ary_new3(1, i);
    
  •        }
    
  •    }
    
  • }
  • return Qnil;
    +}

+static VALUE
+chunk_by_i(VALUE yielder, VALUE enumerator, int argc, VALUE *argv)
+{

  • VALUE enumerable;
  • struct chunk_by_arg arg;
  • enumerable = rb_ivar_get(enumerator,
    rb_intern(“chunk_by_enumerable”));
  • arg.categorize = rb_ivar_get(enumerator,
    rb_intern(“chunk_by_categorize”));
  • arg.prev_value = Qnil;
  • arg.prev_elts = Qnil;
  • arg.yielder = yielder;
  • rb_block_call(enumerable, id_each, 0, 0, chunk_by_ii, (VALUE)&arg);
  • if (!NIL_P(arg.prev_elts))
  •    rb_funcall(arg.yielder, rb_intern("<<"), 1, arg.prev_elts);
    
  • return Qnil;
    +}

+/*

    • call-seq:
    • enum.chunk_by {|elt| ... } => enumerator
      
    • Creates an enumerator for iterating chunked elements of enum.
    • This method gathers consecutive elements which
    • the blocks returns a same value.
    • The following values has special meaning:
      • nil, false and :_separator specifies that gathered elements is
        not yielded.
      • :_singleton specifies the element should be gathered only itself.
    • Other symbols which begins an underscore may be used in future.
    • (1…10).chunk_by {|n| n & 2 }.each {|a| p a }
    • #=> [1] # 1 & 2 = 0
    • [2, 3] # 2 & 2 = 3 & 2 = 1

    • [4, 5] # 4 & 2 = 5 & 2 = 0

    • [6, 7] # 6 & 2 = 7 & 2 = 1

    • [8, 9] # 8 & 2 = 9 & 2 = 0

    • [10] # 10 & 2 = 1

    • gather indented blocks.

    • io.chunk_by {|line| /\A\s/ =~ line }.each {|lines| pp lines }
  • */
    +static VALUE
    +enum_chunk_by(VALUE enumerable)
    +{
  • VALUE enumerator = rb_obj_alloc(rb_cEnumerator);
  • rb_ivar_set(enumerator, rb_intern(“chunk_by_enumerable”),
    enumerable);
  • rb_ivar_set(enumerator, rb_intern(“chunk_by_categorize”),
    rb_block_proc());
  • rb_block_call(enumerator, rb_intern(“initialize”), 0, 0,
    chunk_by_i, enumerator);
  • return enumerator;
    +}

+struct slice_before_arg {

  • VALUE separator_p;
  • VALUE prev_elts;
  • VALUE yielder;
    +};

+static VALUE
+slice_before_ii(VALUE i, VALUE _argp, int argc, VALUE *argv)
+{

  • struct slice_before_arg *argp = (struct slice_before_arg *)_argp;
  • ENUM_WANT_SVALUE();
  • if (RTEST(rb_funcall(argp->separator_p, rb_intern(“call”), 1, i)))
    {
  •    if (!NIL_P(argp->prev_elts))
    
  •        rb_funcall(argp->yielder, rb_intern("<<"), 1, 
    

argp->prev_elts);

  •    argp->prev_elts = rb_ary_new3(1, i);
    
  • }
  • else {
  •    if (NIL_P(argp->prev_elts))
    
  •        argp->prev_elts = rb_ary_new3(1, i);
    
  •    else
    
  •        rb_ary_push(argp->prev_elts, i);
    
  • }
  • return Qnil;
    +}

+static VALUE
+slice_before_i(VALUE yielder, VALUE enumerator, int argc, VALUE *argv)
+{

  • VALUE enumerable;
  • struct slice_before_arg arg;
  • enumerable = rb_ivar_get(enumerator,
    rb_intern(“slice_before_enumerable”));
  • arg.separator_p = rb_ivar_get(enumerator,
    rb_intern(“slice_before_separator_p”));
  • arg.prev_elts = Qnil;
  • arg.yielder = yielder;
  • rb_block_call(enumerable, id_each, 0, 0, slice_before_ii,
    (VALUE)&arg);
  • if (!NIL_P(arg.prev_elts))
  •    rb_funcall(arg.yielder, rb_intern("<<"), 1, arg.prev_elts);
    
  • return Qnil;
    +}

+static VALUE
+enum_slice_before(VALUE enumerable)
+{

  • VALUE enumerator = rb_obj_alloc(rb_cEnumerator);
  • rb_ivar_set(enumerator, rb_intern(“slice_before_enumerable”),
    enumerable);
  • rb_ivar_set(enumerator, rb_intern(“slice_before_separator_p”),
    rb_block_proc());
  • rb_block_call(enumerator, rb_intern(“initialize”), 0, 0,
    slice_before_i, enumerator);
  • return enumerator;
    +}

/*

  • The Enumerable mixin provides collection classes with
  • several traversal and searching methods, and with the ability to
    @@ -1852,6 +2016,9 @@ Init_Enumerable(void)
    rb_define_method(rb_mEnumerable, “drop”, enum_drop, 1);
    rb_define_method(rb_mEnumerable, “drop_while”, enum_drop_while, 0);
    rb_define_method(rb_mEnumerable, “cycle”, enum_cycle, -1);
  • rb_define_method(rb_mEnumerable, “gather”, enum_chunk_by, 0);

  • rb_define_method(rb_mEnumerable, “chunk_by”, enum_chunk_by, 0);

  • rb_define_method(rb_mEnumerable, “slice_before”, enum_slice_before,
    0);

    id_eqq = rb_intern(“===”);
    id_each = rb_intern(“each”);

e$B@.@%$G$9!#e(B

Tanaka A. wrote:

e$B$r8F$V$3$H$,$G$-$F$7$^$$$^$9$M!#e(B

e$B>uBV$r07$&$H$3$l$OLdBj$r@8$8$k$+$b$7$l$^$;$s!#e(B

e$B$3$NE@$r9M$($k$H!“e(Bbuffer e$B$Ne(B status e$B$K$O0UL#$,$”$C$?!"$H$$$&e(B
e$B$3$H$+$J$!!#e(B

Enumerator#each_with_object
e$B$N$h$&$K!“e(Bgather_with_objecte$B$”$?$j$re(B
e$BDs6!$9$l$P$=$l$GB-$j$^$;$s$+$M!#e(B

e$B$"$^$jGI@8%a%=%C%I$,A}$($9$.$k$N$bA1$70-$7$G$O$"$j$^$9$,!#e(B

In article [email protected],
“NARUSE, Yui” [email protected] writes:

Enumerator#each_with_object e$B$N$h$&$K!“e(Bgather_with_objecte$B$”$?$j$re(B
e$BDs6!$9$l$P$=$l$GB-$j$^$;$s$+$M!#e(B

e$B2r7h$9$k$N$KJL$N%a%=%C%I$K$9$kI,MW$O$J$$$G$7$g$&!#0z?t$rM?$(e(B
e$B$?$i$=$l$,e(B (each e$B$N$?$S$Ke(B dup e$B$5$l$Fe(B)
e$B$^$H$^$j$rH=CG$9$k%V%m%Ce(B
e$B%/$NBhe(B2e$B0z?t$K$o$?$9$H$+!#e(B

e$B$?$H$($P!"e(Bmbox e$B$NJ,3d$G!"6u9T$N8e$Ne(B "From "
e$B$GJ,3d$9$k$J$i!"e(B
e$B0J2<$N$h$&$K$9$k$H$+!#e(B

ARGF.slice_before(emp: true) {|line,h|
prevemp = h[:emp]
h[:emp] = line == “\n”
prevemp && line.start_with?("From ")
}.each {|x| pp x }

e$B$`$7$m;d$,LB$C$F$$$k$N$O$3$l$O2r7h$9$kI,MW$,$“$kLdBj$J$N$+$Ie(B
e$B$&$+!”$H$$$&$H$3$m$G$9!#e(B

e$B8=;~E@$G$O2r7h$7$F$b$$$$$s$8$c$J$$$+$H$$$&5$J,$K79$$$F$$$^$9e(B
e$B$,!#e(B

e$B<BAu$OFq$7$/$J$/$F0J2<$N$h$&$K$G$-$^$9!#e(B

% svn diff --diff-cmd diff -x ‘-u -p’
Index: enum.c

— enum.c (revision 23381)
+++ enum.c (working copy)
@@ -1793,6 +1793,231 @@ enum_cycle(int argc, VALUE argv, VALUE
return Qnil; /
not reached */
}

+struct chunk_by_arg {

  • VALUE categorize;
  • VALUE state;
  • VALUE prev_value;
  • VALUE prev_elts;
  • VALUE yielder;
    +};

+static VALUE
+chunk_by_ii(VALUE i, VALUE _argp, int argc, VALUE *argv)
+{

  • struct chunk_by_arg *argp = (struct chunk_by_arg *)_argp;
  • VALUE v;
  • VALUE singleton = ID2SYM(rb_intern(“_singleton”));
  • VALUE separator = ID2SYM(rb_intern(“_separator”));
  • ENUM_WANT_SVALUE();
  • v = rb_funcall(argp->categorize, rb_intern(“call”), 2, i,
    argp->state);
  • if (v == singleton) {
  •    if (!NIL_P(argp->prev_value)) {
    
  •        rb_funcall(argp->yielder, rb_intern("<<"), 1, 
    

argp->prev_elts);

  •        argp->prev_value = argp->prev_elts = Qnil;
    
  •    }
    
  •    rb_funcall(argp->yielder, rb_intern("<<"), 1, rb_ary_new3(1, 
    

i));

  • }
  • else if (!RTEST(v) || v == separator) {
  •    if (!NIL_P(argp->prev_value)) {
    
  •        rb_funcall(argp->yielder, rb_intern("<<"), 1, 
    

argp->prev_elts);

  •        argp->prev_value = argp->prev_elts = Qnil;
    
  •    }
    
  • }
  • else if (SYMBOL_P(v) && rb_id2name(SYM2ID(v))[0] == ‘_’) {
  • rb_raise(rb_eRuntimeError, “symbol begins with an underscore is
    reserved”);
  • }
  • else {
  •    if (NIL_P(argp->prev_value)) {
    
  •        argp->prev_value = v;
    
  •        argp->prev_elts = rb_ary_new3(1, i);
    
  •    }
    
  •    else {
    
  •        if (rb_equal(argp->prev_value, v)) {
    
  •            rb_ary_push(argp->prev_elts, i);
    
  •        }
    
  •        else {
    
  •            rb_funcall(argp->yielder, rb_intern("<<"), 1, 
    

argp->prev_elts);

  •            argp->prev_value = v;
    
  •            argp->prev_elts = rb_ary_new3(1, i);
    
  •        }
    
  •    }
    
  • }
  • return Qnil;
    +}

+static VALUE
+chunk_by_i(VALUE yielder, VALUE enumerator, int argc, VALUE *argv)
+{

  • VALUE enumerable;
  • struct chunk_by_arg arg;
  • enumerable = rb_ivar_get(enumerator,
    rb_intern(“chunk_by_enumerable”));
  • arg.categorize = rb_ivar_get(enumerator,
    rb_intern(“chunk_by_categorize”));
  • arg.state = rb_ivar_get(enumerator,
    rb_intern(“chunk_by_initial_state”));
  • arg.prev_value = Qnil;
  • arg.prev_elts = Qnil;
  • arg.yielder = yielder;
  • if (!NIL_P(arg.state))
  •    arg.state = rb_obj_dup(arg.state);
    
  • rb_block_call(enumerable, id_each, 0, 0, chunk_by_ii, (VALUE)&arg);
  • if (!NIL_P(arg.prev_elts))
  •    rb_funcall(arg.yielder, rb_intern("<<"), 1, arg.prev_elts);
    
  • return Qnil;
    +}

+/*

    • call-seq:
    • enum.chunk_by(initial_state=nil) {|elt, state| ... } => 
      

enumerator

    • Creates an enumerator for iterating chunked elements of enum.
    • This method gathers consecutive elements which
    • the block returns a same value.
    • The following values has special meaning:
      • nil, false and :_separator specifies that gathered elements is
        not yielded.
      • :_singleton specifies the element should be gathered only itself.
    • Other symbols which begins an underscore may be used in future.
    • If non-nil value is given for initial_state,
    • it is duplicated for each “each” method invocation of the
      enumerator.
    • The duplicated object is passed to second argument of the block for
      “chunk_by” method…
    • (1…10).chunk_by {|n| n & 2 }.each {|a| p a }
    • #=> [1] # 1 & 2 = 0
    • [2, 3] # 2 & 2 = 3 & 2 = 1

    • [4, 5] # 4 & 2 = 5 & 2 = 0

    • [6, 7] # 6 & 2 = 7 & 2 = 1

    • [8, 9] # 8 & 2 = 9 & 2 = 0

    • [10] # 10 & 2 = 1

    • gather indented blocks.

    • io.chunk_by {|line| /\A\s/ =~ line }.each {|lines| pp lines }
    • iterate over svn log entries.

    • IO.popen([{“LANG”=>“C”}, *%w[svn log enum.c]]) {|f|
    •  sep = "-"*72+"\n"
      
    •  f.chunk_by {|line| line != sep }.each {|e| pp e }
      
    • }
  • */
    +static VALUE
    +enum_chunk_by(int argc, VALUE *argv, VALUE enumerable)
    +{
  • VALUE initial_state;
  • VALUE enumerator;
  • rb_scan_args(argc, argv, “01”, &initial_state);
  • enumerator = rb_obj_alloc(rb_cEnumerator);
  • rb_ivar_set(enumerator, rb_intern(“chunk_by_enumerable”),
    enumerable);
  • rb_ivar_set(enumerator, rb_intern(“chunk_by_categorize”),
    rb_block_proc());
  • rb_ivar_set(enumerator, rb_intern(“chunk_by_initial_state”),
    initial_state);
  • rb_block_call(enumerator, rb_intern(“initialize”), 0, 0,
    chunk_by_i, enumerator);
  • return enumerator;
    +}

+struct slice_before_arg {

  • VALUE separator_p;
  • VALUE state;
  • VALUE prev_elts;
  • VALUE yielder;
    +};

+static VALUE
+slice_before_ii(VALUE i, VALUE _argp, int argc, VALUE *argv)
+{

  • struct slice_before_arg *argp = (struct slice_before_arg *)_argp;
  • VALUE bool;
  • ENUM_WANT_SVALUE();
  • bool = rb_funcall(argp->separator_p, rb_intern(“call”), 2, i,
    argp->state);
  • if (RTEST(bool)) {
  •    if (!NIL_P(argp->prev_elts))
    
  •        rb_funcall(argp->yielder, rb_intern("<<"), 1, 
    

argp->prev_elts);

  •    argp->prev_elts = rb_ary_new3(1, i);
    
  • }
  • else {
  •    if (NIL_P(argp->prev_elts))
    
  •        argp->prev_elts = rb_ary_new3(1, i);
    
  •    else
    
  •        rb_ary_push(argp->prev_elts, i);
    
  • }
  • return Qnil;
    +}

+static VALUE
+slice_before_i(VALUE yielder, VALUE enumerator, int argc, VALUE *argv)
+{

  • VALUE enumerable;
  • struct slice_before_arg arg;
  • enumerable = rb_ivar_get(enumerator,
    rb_intern(“slice_before_enumerable”));
  • arg.separator_p = rb_ivar_get(enumerator,
    rb_intern(“slice_before_separator_p”));
  • arg.state = rb_ivar_get(enumerator,
    rb_intern(“slice_before_initial_state”));
  • arg.prev_elts = Qnil;
  • arg.yielder = yielder;
  • if (!NIL_P(arg.state))
  •    arg.state = rb_obj_dup(arg.state);
    
  • rb_block_call(enumerable, id_each, 0, 0, slice_before_ii,
    (VALUE)&arg);
  • if (!NIL_P(arg.prev_elts))
  •    rb_funcall(arg.yielder, rb_intern("<<"), 1, arg.prev_elts);
    
  • return Qnil;
    +}

+/*

    • call-seq:
    • enum.slice_before(initial_state=nil) {|elt, state| ... } => 
      

enumerator

    • Creates an enumerator for iterating gathered elements of enum.
    • This method gathers consecutive elements which
    • the block returns a true for the starting element.
    • If non-nil value is given for initial_state,
    • it is duplicated for each “each” method invocation of the
      enumerator.
    • The duplicated object is passed to second argument of the block for
      “chunk_by” method…
    • iterate over ChangeLog entries.

    • open(“ChangeLog”) {|f|
    •  f.slice_before {|line| /\A\S/ =~ line }.each {|e| pp e}
      
    • }
    • split mails in mbox (slice before Unix From line after an empty

line)

    • open(“mbox”) {|f|
    •  f.slice_before(emp: true) {|line,h|
      
    •  prevemp = h[:emp]
      
    •  h[:emp] = line == "\n"
      
    •  prevemp && line.start_with?("From ")
      
    • }.each {|mail|
    •  pp mail
      
    • }
  • */
    +static VALUE
    +enum_slice_before(int argc, VALUE *argv, VALUE enumerable)
    +{
  • VALUE initial_state, enumerator;
  • rb_scan_args(argc, argv, “01”, &initial_state);
  • enumerator = rb_obj_alloc(rb_cEnumerator);
  • rb_ivar_set(enumerator, rb_intern(“slice_before_enumerable”),
    enumerable);
  • rb_ivar_set(enumerator, rb_intern(“slice_before_separator_p”),
    rb_block_proc());
  • rb_ivar_set(enumerator, rb_intern(“slice_before_initial_state”),
    initial_state);
  • rb_block_call(enumerator, rb_intern(“initialize”), 0, 0,
    slice_before_i, enumerator);
  • return enumerator;
    +}

/*

  • The Enumerable mixin provides collection classes with
  • several traversal and searching methods, and with the ability to
    @@ -1852,6 +2077,9 @@ Init_Enumerable(void)
    rb_define_method(rb_mEnumerable, “drop”, enum_drop, 1);
    rb_define_method(rb_mEnumerable, “drop_while”, enum_drop_while, 0);
    rb_define_method(rb_mEnumerable, “cycle”, enum_cycle, -1);
  • rb_define_method(rb_mEnumerable, “gather”, enum_chunk_by, -1);

  • rb_define_method(rb_mEnumerable, “chunk_by”, enum_chunk_by, -1);

  • rb_define_method(rb_mEnumerable, “slice_before”, enum_slice_before,
    -1);

    id_eqq = rb_intern(“===”);
    id_each = rb_intern(“each”);

In article [email protected],
Tanaka A. [email protected] writes:

Python e$B$N$h$&$K!"e(Beach e$B$N%V%m%C%/$NBh0l0z?t$KEO$7$F$b$$$$$N$+e(B
e$B$b$7$l$^$;$s!#e(B

enum.gather(init_state) {|elt,state| … }.each {|category,ary| … }

e$B$3$&$9$k$H!"$3$l$,e(B group_by e$B$H;w$F$$$k$3$H$,$h$/$o$+$j$^$9!#e(B

% ./ruby -e ‘(1…10).gather {|e| e & 4 }.each {|x| p x }’
[0, [1, 2, 3]]
[4, [4, 5, 6, 7]]
[0, [8, 9, 10]]
% ./ruby -e ‘(1…10).group_by {|e| e & 4 }.each {|x| p x }’
[0, [1, 2, 3, 8, 9, 10]]
[4, [4, 5, 6, 7]]

gather e$B$Oe(B 1, 2, 3 e$B$He(B 8, 9, 10 e$B$,$o$+$l$F$$$^$9$,!"e(B
group_by e$B$O:G8e$^$G$_$k$N$G$=$l$i$,$^$H$^$C$F$$$^$9!#e(B

e$B$^$!!"JV$jCM$,e(B Enumerator e$B$He(B Hash
e$B$H0c$C$?$j$b$9$k$N$G!"A4BNe(B
e$B$H$7$F;w$F$$$k$+$H$$$&$H$^$?JL$G$9$,!#e(B

e$B$H$$$&$h$&$K<BAu$7$F$_$?$b$N$r$D$1$F$*$-$^$9!#e(B

% svn diff --diff-cmd diff -x ‘-u -p’
Index: enum.c

— enum.c (revision 23381)
+++ enum.c (working copy)
@@ -1793,6 +1793,247 @@ enum_cycle(int argc, VALUE argv, VALUE
return Qnil; /
not reached */
}

+struct chunk_by_arg {

  • VALUE categorize;
  • VALUE state;
  • VALUE prev_value;
  • VALUE prev_elts;
  • VALUE yielder;
    +};

+static VALUE
+chunk_by_ii(VALUE i, VALUE _argp, int argc, VALUE *argv)
+{

  • struct chunk_by_arg *argp = (struct chunk_by_arg *)_argp;
  • VALUE v;
  • VALUE singleton = ID2SYM(rb_intern(“_singleton”));
  • VALUE separator = ID2SYM(rb_intern(“_separator”));
  • ENUM_WANT_SVALUE();
  • v = rb_funcall(argp->categorize, rb_intern(“call”), 2, i,
    argp->state);
  • if (v == singleton) {
  •    if (!NIL_P(argp->prev_value)) {
    
  •        rb_funcall(argp->yielder, rb_intern("<<"), 1, 
    

rb_assoc_new(argp->prev_value, argp->prev_elts));

  •        argp->prev_value = argp->prev_elts = Qnil;
    
  •    }
    
  •    rb_funcall(argp->yielder, rb_intern("<<"), 1, rb_assoc_new(v, 
    

rb_ary_new3(1, i)));

  • }
  • else if (!RTEST(v) || v == separator) {
  •    if (!NIL_P(argp->prev_value)) {
    
  •        rb_funcall(argp->yielder, rb_intern("<<"), 1, 
    

rb_assoc_new(argp->prev_value, argp->prev_elts));

  •        argp->prev_value = argp->prev_elts = Qnil;
    
  •    }
    
  • }
  • else if (SYMBOL_P(v) && rb_id2name(SYM2ID(v))[0] == ‘_’) {
  • rb_raise(rb_eRuntimeError, “symbol begins with an underscore is
    reserved”);
  • }
  • else {
  •    if (NIL_P(argp->prev_value)) {
    
  •        argp->prev_value = v;
    
  •        argp->prev_elts = rb_ary_new3(1, i);
    
  •    }
    
  •    else {
    
  •        if (rb_equal(argp->prev_value, v)) {
    
  •            rb_ary_push(argp->prev_elts, i);
    
  •        }
    
  •        else {
    
  •            rb_funcall(argp->yielder, rb_intern("<<"), 1, 
    

rb_assoc_new(argp->prev_value, argp->prev_elts));

  •            argp->prev_value = v;
    
  •            argp->prev_elts = rb_ary_new3(1, i);
    
  •        }
    
  •    }
    
  • }
  • return Qnil;
    +}

+static VALUE
+chunk_by_i(VALUE yielder, VALUE enumerator, int argc, VALUE *argv)
+{

  • VALUE enumerable;
  • struct chunk_by_arg arg;
  • enumerable = rb_ivar_get(enumerator,
    rb_intern(“chunk_by_enumerable”));
  • arg.categorize = rb_ivar_get(enumerator,
    rb_intern(“chunk_by_categorize”));
  • arg.state = rb_ivar_get(enumerator,
    rb_intern(“chunk_by_initial_state”));
  • arg.prev_value = Qnil;
  • arg.prev_elts = Qnil;
  • arg.yielder = yielder;
  • if (!NIL_P(arg.state))
  •    arg.state = rb_obj_dup(arg.state);
    
  • rb_block_call(enumerable, id_each, 0, 0, chunk_by_ii, (VALUE)&arg);
  • if (!NIL_P(arg.prev_elts))
  •    rb_funcall(arg.yielder, rb_intern("<<"), 1, 
    

rb_assoc_new(arg.prev_value, arg.prev_elts));

  • return Qnil;
    +}

+/*

    • call-seq:
    • enum.chunk_by(initial_state=nil) {|elt, state| ... } => 
      

enumerator

    • Creates an enumerator for iterating chunked elements of enum.
    • This method gathers consecutive elements which
    • the block returns a same value.
    • The following values has special meaning:
      • nil, false and :_separator specifies that gathered elements is
        not yielded.
      • :_singleton specifies the element should be gathered only itself.
    • Other symbols which begins an underscore may be used in future.
    • If non-nil value is given for initial_state,
    • it is duplicated for each “each” method invocation of the
      enumerator.
    • The duplicated object is passed to second argument of the block for
      “chunk_by” method…
    • (1…10).chunk_by {|n| n & 2 }.each {|a| p a }
    • #=> [1] # 1 & 2 = 0
    • [2, 3] # 2 & 2 = 3 & 2 = 1

    • [4, 5] # 4 & 2 = 5 & 2 = 0

    • [6, 7] # 6 & 2 = 7 & 2 = 1

    • [8, 9] # 8 & 2 = 9 & 2 = 0

    • [10] # 10 & 2 = 1

    • gather indented blocks.

    • io.chunk_by {|line| /\A\s/ =~ line }.each {|lines| pp lines }
    • iterate over svn log entries.

    • IO.popen([{“LANG”=>“C”}, *%w[svn log enum.c]]) {|f|
    •  sep = "-"*72+"\n"
      
    •  f.chunk_by {|line| line != sep }.each {|e| pp e }
      
    • }
  • */
    +static VALUE
    +enum_chunk_by(int argc, VALUE *argv, VALUE enumerable)
    +{
  • VALUE initial_state;
  • VALUE enumerator;
  • rb_scan_args(argc, argv, “01”, &initial_state);
  • enumerator = rb_obj_alloc(rb_cEnumerator);
  • rb_ivar_set(enumerator, rb_intern(“chunk_by_enumerable”),
    enumerable);
  • rb_ivar_set(enumerator, rb_intern(“chunk_by_categorize”),
    rb_block_proc());
  • rb_ivar_set(enumerator, rb_intern(“chunk_by_initial_state”),
    initial_state);
  • rb_block_call(enumerator, rb_intern(“initialize”), 0, 0,
    chunk_by_i, enumerator);
  • return enumerator;
    +}

+struct slice_before_arg {

  • VALUE separator_p;
  • VALUE state;
  • VALUE prev_elts;
  • VALUE yielder;
    +};

+static VALUE
+slice_before_ii(VALUE i, VALUE _argp, int argc, VALUE *argv)
+{

  • struct slice_before_arg *argp = (struct slice_before_arg *)_argp;
  • VALUE bool;
  • ENUM_WANT_SVALUE();
  • bool = rb_funcall(argp->separator_p, rb_intern(“call”), 2, i,
    argp->state);
  • if (RTEST(bool)) {
  •    if (!NIL_P(argp->prev_elts))
    
  •        rb_funcall(argp->yielder, rb_intern("<<"), 1, 
    

argp->prev_elts);

  •    argp->prev_elts = rb_ary_new3(1, i);
    
  • }
  • else {
  •    if (NIL_P(argp->prev_elts))
    
  •        argp->prev_elts = rb_ary_new3(1, i);
    
  •    else
    
  •        rb_ary_push(argp->prev_elts, i);
    
  • }
  • return Qnil;
    +}

+static VALUE
+slice_before_i(VALUE yielder, VALUE enumerator, int argc, VALUE *argv)
+{

  • VALUE enumerable;
  • struct slice_before_arg arg;
  • enumerable = rb_ivar_get(enumerator,
    rb_intern(“slice_before_enumerable”));
  • arg.separator_p = rb_ivar_get(enumerator,
    rb_intern(“slice_before_separator_p”));
  • arg.state = rb_ivar_get(enumerator,
    rb_intern(“slice_before_initial_state”));
  • arg.prev_elts = Qnil;
  • arg.yielder = yielder;
  • if (!NIL_P(arg.state))
  •    arg.state = rb_obj_dup(arg.state);
    
  • rb_block_call(enumerable, id_each, 0, 0, slice_before_ii,
    (VALUE)&arg);
  • if (!NIL_P(arg.prev_elts))
  •    rb_funcall(arg.yielder, rb_intern("<<"), 1, arg.prev_elts);
    
  • return Qnil;
    +}

+/*

    • call-seq:
    • enum.slice_before(initial_state=nil) {|elt, state| ... } => 
      

enumerator

    • Creates an enumerator for iterating gathered elements of enum.
    • This method gathers consecutive elements which
    • the block returns a true for the starting element.
    • If non-nil value is given for initial_state,
    • it is duplicated for each “each” method invocation of the
      enumerator.
    • The duplicated object is passed to second argument of the block for
      “chunk_by” method…
    • iterate over ChangeLog entries.

    • open(“ChangeLog”) {|f|
    •  f.slice_before {|line| /\A\S/ =~ line }.each {|e| pp e}
      
    • }
    • parse mbox

    • open(“mbox”) {|f|
    •  f.slice_before {|line|
      
    •    line.start_with? "From "
      
    •  }.each {|mail|
      
    •    unix_from = mail.shift
      
    •    i = mail.index("\n")
      
    •    header = mail[0...i]
      
    •    body = mail[(i+1)..-1]
      
    •    fields = header.slice_before {|line| !" \t".include?(line[0]) 
      

}.to_a

    •    p unix_from
      
    •    pp fields
      
    •    pp body
      
    •  }
      
    • }
    • split mails in mbox (slice before Unix From line after an empty

line)

    • open(“mbox”) {|f|
    •  f.slice_before(emp: true) {|line,h|
      
    •  prevemp = h[:emp]
      
    •  h[:emp] = line == "\n"
      
    •  prevemp && line.start_with?("From ")
      
    • }.each {|mail|
    •  pp mail
      
    • }
  • */
    +static VALUE
    +enum_slice_before(int argc, VALUE *argv, VALUE enumerable)
    +{
  • VALUE initial_state, enumerator;
  • rb_scan_args(argc, argv, “01”, &initial_state);
  • enumerator = rb_obj_alloc(rb_cEnumerator);
  • rb_ivar_set(enumerator, rb_intern(“slice_before_enumerable”),
    enumerable);
  • rb_ivar_set(enumerator, rb_intern(“slice_before_separator_p”),
    rb_block_proc());
  • rb_ivar_set(enumerator, rb_intern(“slice_before_initial_state”),
    initial_state);
  • rb_block_call(enumerator, rb_intern(“initialize”), 0, 0,
    slice_before_i, enumerator);
  • return enumerator;
    +}

/*

  • The Enumerable mixin provides collection classes with
  • several traversal and searching methods, and with the ability to
    @@ -1852,6 +2093,9 @@ Init_Enumerable(void)
    rb_define_method(rb_mEnumerable, “drop”, enum_drop, 1);
    rb_define_method(rb_mEnumerable, “drop_while”, enum_drop_while, 0);
    rb_define_method(rb_mEnumerable, “cycle”, enum_cycle, -1);
  • rb_define_method(rb_mEnumerable, “gather”, enum_chunk_by, -1);

  • rb_define_method(rb_mEnumerable, “chunk_by”, enum_chunk_by, -1);

  • rb_define_method(rb_mEnumerable, “slice_before”, enum_slice_before,
    -1);

    id_eqq = rb_intern(“===”);
    id_each = rb_intern(“each”);

In article [email protected],
Yukihiro M. [email protected] writes:

e$B;d$b$=$&;W$$$^$9!#$?$@!"e(BEnumerablee$B$+$i!V>r7o$rK~$?$98B$jO"B3e(B
e$B$7$?MWAG$r$^$H$a$k!W$3$H$re(Bgathere$B$H8F$V$3$H$K$J$s$H$J$/K~B-$Ge(B
e$B$-$^$;$s!#e(Bgathere$B$H$$$&C18l$+$i$OA4BN$+$i=8$a$k$h$&$J0u>]$r<ue(B
e$B$1$^$;$s$+!)e(B e$B;d$@$1$N463P$G$7$g$&$+!#e(B

gather e$B$O!V!RJ*$r!S0l$D0l$D=&$&!W$H$$$C$?0UL#$,$“$k$N$G!”$=e(B
e$B$N7|G0$O@5$7$$$+$b$7$l$^$;$s!#7k2L$N$^$H$^$j$,!"$b$H$b$H$^$He(B
e$B$^$C$F$$$?$H$O8B$i$J$$$N$O$=$&$J$N$G$7$g$&!#e(B

e$B$G!"$7$P$i$/9M$($?$N$G$9$,!“e(Bchunk e$B$J$$$7e(B chunk_by
e$B$O$I$&$G$7$ge(B
e$B$&$+!#$3$C$A$O!V!J%Q%s!&Fy!&@PC:$J$I$N!KBg$-$$$+$?$^$je(B, e$B$V$De(B
e$B@Z$je(B,e$B8|@Z$j!W$J$N$G!”@Z$j=P$7$?$b$N$O$b$H$b$H$/$C$D$$$F$$$?e(B
e$B0UL#$,$H$l$k$h$&$K;W$$$^$9!#e(B

e$B1sF#$G$9!#e(B

gather e$B$O4pK\E*$K;?@.$J$s$G$9$,!“e(Bnil e$B$NFCJL07$$$,$A$g$C$He(B
e$B5$;}$A0-$$$G$9!#%”%I%[%C%/$H$$$&$+!“%7%s%W%k$JL>A0$KH?$7$Fe(B
e$B5!G=$,5M$a9~$^$l$9$.$F$$$k!”$H$$$&0u>]$G$9!#e(B

e$B$=$&$$$&E@$G!"e(B

2009/05/14 11:14 Tanaka A. [email protected]:

Python e$B$N$h$&$K!"e(Beach e$B$N%V%m%C%/$NBh0l0z?t$KEO$7$F$b$$$$$N$+e(B
e$B$b$7$l$^$;$s!#e(B

enum.gather(init_state) {|elt,state| … }.each {|category,ary| … }

e$B$3$N%$%s%?!<%U%'%$%9$O$H$F$b$$$$$H;W$$$^$9!#e(B

enum.gather {|elt| … }.select {|cat, ary| cat }

e$B$H<+A3$KJ,N%$G$-$^$9!#e(B
e$B<N$F$kItJ,$^$Ge(B ary e$B$r:n$k$N$,B?>/5$$K$J$k$+$b$7$l$^$;$s$,!"e(B
nil e$B$NFCJL07$$$^$G$9$k%a%=%C%I$Oe(B gather e$B$h$jD9$/$FL@3N$Je(B
e$B%a%=%C%IL>$GJL$KMQ0U$7$?J}$,$$$$$H;W$$$^$7$?!#e(B

e$B$^$H$^$j$+$1$N5DO@$r0z$CA_$-2s$9$h$&$G?=$7Lu$"$j$^$;$s$,!"e(B
e$B0l1~0U8+$G$7$?!#e(B

e$B$J$*!“e(BEnumerable#group_by e$B$O$9$G$K$”$k$N$G!"L>A0$K$D$$$F??;we(B
e$B$G$-$J$$$N$O;DG0$J$H$3$m$G$9!#e(B

e$B$H$F$b;DG0$G$9!#e(B
e$B$G$be(B chunk e$B$Oe(B (e$BHs%M%$%F%#%VE*$K$Oe(B)
e$B$h$5$2$KJ9$3$($^$7$?!#e(B

e$B$^$D$b$He(B e$B$f$-$R$m$G$9e(B

In message “Re: [ruby-dev:38450] Re: Enumerable#gather_each”
on Thu, 14 May 2009 23:40:41 +0900, Tanaka A. [email protected]
writes:

|e$B$G!"$7$P$i$/9M$($?$N$G$9$,!“e(Bchunk e$B$J$$$7e(B chunk_by e$B$O$I$&$G$7$ge(B
|e$B$&$+!#$3$C$A$O!V!J%Q%s!&Fy!&@PC:$J$I$N!KBg$-$$$+$?$^$je(B, e$B$V$De(B
|e$B@Z$je(B,e$B8|@Z$j!W$J$N$G!”@Z$j=P$7$?$b$N$O$b$H$b$H$/$C$D$$$F$$$?e(B
|e$B0UL#$,$H$l$k$h$&$K;W$$$^$9!#e(B

chunke$B$K$O$3$N0UL#$GF0;l$K$J$i$J$$$N$,5$$K$J$j$^$9!#e(B
e$B$9$G$K=P$F$$$?e(Bslice_bye$B$H$+$,$$$$$h$&$J5$$b$7$^$9!#e(B

e$B$,!"EDCf$5$s$,Ds0F$7$F$$$k5!G=A4Ite(B(e$BMWAG:o=|$H$+e(B)e$B$Oe(Bslice_bye$B$Ge(B
e$B$OI=8=$7$-$l$J$$$h$&$J5$$b$7$^$9$,!#e(B

Haskell e$B$Ne(B groupBy e$B$He(B Python e$B$Ne(B groupby
e$B$,;w$F$$$k!"$H$$$&OCe(B
e$B$r65$($F$b$i$C$?$N$G!"D4$Y$F;W$C$?$s$G$9$,!"e(B

In article [email protected],
Tanaka A. [email protected] writes:

ary.category e$B$H$+$G$$$$$s$8$c$J$$$G$7$g$&$+!#G[Ns$K%a%=%C%Ie(B
e$B$,$"$C$?$+$i$C$F<YKb$K$O$J$j$^$;$s$h$M!#e(B

enum.gather { … }.each {|ary| p ary.category }

Python e$B$N$h$&$K!"e(Beach e$B$N%V%m%C%/$NBh0l0z?t$KEO$7$F$b$$$$$N$+e(B
e$B$b$7$l$^$;$s!#e(B

enum.gather(init_state) {|elt,state| … }.each {|category,ary| … }

e$B$J$*!“e(BEnumerable#group_by e$B$O$9$G$K$”$k$N$G!"L>A0$K$D$$$F??;we(B
e$B$G$-$J$$$N$O;DG0$J$H$3$m$G$9!#e(B

In article [email protected],
Yukihiro M. [email protected] writes:

chunke$B$K$O$3$N0UL#$GF0;l$K$J$i$J$$$N$,5$$K$J$j$^$9!#e(B
e$B$9$G$K=P$F$$$?e(Bslice_bye$B$H$+$,$$$$$h$&$J5$$b$7$^$9!#e(B

slice_by e$B$,5$$KF~$i$J$$$H$$$&$o$1$G$O$"$j$^$;$s$,!"e(B

e$B$Ge(B chunk e$B$r$R$/$He(B

  • verb (used with object)
  1.  to cut, break, or form into chunks: Chunk that wedge of cheese 
    

and put the pieces on a plate.

e$B$H$“$k$N$G!”$=$N0UL#$G$NF0;l$b$“$k$H$$$&8+J}$b$”$k$h$&$G$9!#e(B

e$B$,!"EDCf$5$s$,Ds0F$7$F$$$k5!G=A4Ite(B(e$BMWAG:o=|$H$+e(B)e$B$Oe(Bslice_bye$B$Ge(B
e$B$OI=8=$7$-$l$J$$$h$&$J5$$b$7$^$9$,!#e(B

e$B>e5-$NB3$-$G!"e(B

  1.  to remove a chunk or chunks from (often fol. by out): Storms 
    

have chunked out the road.

e$B$H$$$&$h$&$K!"=|5n$9$k$H$$$&0UL#$K$b;H$($k$h$&$G$9!#e(B

e$B$J$*!“e(Bslice_by e$B$O!“e(Bslice_after
e$B$H$N4X78$,5$$K$J$j$^$9!#e(B
e$B$`$7$m;w$F$$$k$+$iF1$88l$r;H$&!”$H$$$&$N$b$”$jF@$^$9$,!#e(B

In article
[email protected],
Yusuke ENDOH [email protected] writes:

enum.gather {|elt| … }.select {|cat, ary| cat }

e$B$H<+A3$KJ,N%$G$-$^$9!#e(B
e$B<N$F$kItJ,$^$Ge(B ary e$B$r:n$k$N$,B?>/5$$K$J$k$+$b$7$l$^$;$s$,!"e(B

e$B$b$H$b$HBP>]$,Bg$-$$$H$-$rBP>]$K9M$($F$$$k$N$G!"6=L#$N$J$$Ite(B
e$BJ,$O$J$k$Y$/4JC1$K=hCV$7$?$$$J$!$H;W$C$F$$$^$9!#e(B

e$B$?$H$($P!“e(Bchkbuild e$B$N%m%0$Ge(B minitest
e$B$,%F%9%H$N=gHV$r%i%s%@e(B
e$B%`$KJQ$($k$H$3$m$O%=!<%H$7$F$+$ie(B diff e$B$7$?$$$s$G$9$,!“e(B
http://www.rubyist.net/~akr/chkbuild/debian/ruby-trunk/log/20081213T111000.txt.gz
e$B$N$h$&$K!”%F%9%H0J30$N$H$3$m$,Hs>o$KBg$-$$$3$H$b$”$C$F!“$=$&e(B
e$B$$$&$H$3$m$O4JC1$Ke(B 1e$B9T$:$D=PNO$7$F$$$-$?$$!”$H$$$&%1!<%9$,$"e(B
e$B$j$^$9!#e(B

e$B$=$N$?$a$Ke(B :_singleton e$B$,$“$k$s$G$9$,!”$3$l$,$J$$$He(B

enum.gather {|line| condition ? value : :_singleton }

e$B$H$$$&$N$,e(B

enum.gather(n: 0) {|line| condition ? value : (h[:n] += 1) }

e$B$J$I$H$J$C$F!">uBV$rF3F~$7$J$$$H$$$1$J$$$3$H$K$J$j$^$9!#e(B

e$B$"$kMWAG$rC1FH$G$^$H$^$j$H$9$k$H$$$&H=CG$OMWAGKh$K$G$-$F!“MWe(B
e$BAG4V$N4X78$O$J$$$N$K!”>uBV$r07$o$J$$$H$$$1$J$$$N$O7y$@$J$!!"e(B
e$B$H!#e(B

nil e$B$NFCJL07$$$^$G$9$k%a%=%C%I$Oe(B gather e$B$h$jD9$/$FL@3N$Je(B
e$B%a%=%C%IL>$GJL$KMQ0U$7$?J}$,$$$$$H;W$$$^$7$?!#e(B

e$BFCJL07$$$JCM$rL\N)$?$J$$$H$3$m$K;}$C$F$$$/$3$H$O2DG=$@$H;W$$e(B
e$B$^$9!#e(B

[ruby-dev:38449] e$B$H$+%Q%C%A$NCf$Ne(B rdoc e$B$K$O=q$$$F$"$k$s$G$9e(B
e$B$,!"8=:_!"e(B
nil, false, :_separator e$B$,:o=|$G!"e(B
:_singleton e$B$,C1FH$G$N$^$H$^$j$N;XDj$G$9!#e(B

nil, false e$B$OL\N)$D$N$G!"%7%s%%k$@$1$H$$$&$N$O$R$H$D$N0F$+e(B
e$B$b$7$l$^$;$s!#e(B

e$B$?$@!“e(B[ruby-dev:38394] e$B$N$h$&$J4|BT$KEz$($k$J$i!”$`$7$me(B nil,
false e$B$r:o=|$H$9$k$N$O@5$7$$$N$+$b$7$l$J$$!"$H$b;W$$$^$9!#e(B

e$B1sF#$G$9!#e(B

2009/05/15 14:06 Tanaka A. [email protected]:

e$B$?$H$($P!“e(Bchkbuild e$B$N%m%0$Ge(B minitest e$B$,%F%9%H$N=gHV$r%i%s%@e(B
e$B%`$KJQ$($k$H$3$m$O%=!<%H$7$F$+$ie(B diff e$B$7$?$$$s$G$9$,!“e(B
http://www.rubyist.net/~akr/chkbuild/debian/ruby-trunk/log/20081213T111000.txt.gz
e$B$N$h$&$K!”%F%9%H0J30$N$H$3$m$,Hs>o$KBg$-$$$3$H$b$”$C$F!“$=$&e(B
e$B$$$&$H$3$m$O4JC1$Ke(B 1e$B9T$:$D=PNO$7$F$$$-$?$$!”$H$$$&%1!<%9$,$"e(B
e$B$j$^$9!#e(B

e$B$=$&$$$&%1!<%9$O$h$/$“$k$b$N$J$s$G$7$g$&$+!#e(B
nil e$B$NJ}$O5$L#$,0-$$$b$N$N<{MW$O$”$j$=$&$@$H;W$&$s$G$9$,!“e(B
:_singleton e$B$O$b$C$HIT5$L#$G!”$K$b4X$o$i$:;H$$$I$3$m$,e(B
e$B$"$^$j;W$$$D$-$^$;$s!#e(B

e$B>uBV$r;}$?$;$k$H8@$C$F$b!"$R$I$/J#;($K$O$J$C$F$$$^$;$s$7!“e(B
e$B$=$l$[$IH/@8$7$J$$%1!<%9$J$i$=$N$/$i$$$7$F$b$i$C$F$b$$$$e(B
e$B$+$J!”$H;W$$$^$7$?!#e(B
e$B8DJL$N@Z$jJ,$1J}$KBP1~$7;O$a$?$i%-%j$,$J$$$H$$$&$+!#e(B

e$B$G$9$,!"e(B

[ruby-dev:38449] e$B$H$+%Q%C%A$NCf$Ne(B rdoc e$B$K$O=q$$$F$"$k$s$G$9e(B
e$B$,!"8=:_!"e(B
nil, false, :_separator e$B$,:o=|$G!"e(B
:_singleton e$B$,C1FH$G$N$^$H$^$j$N;XDj$G$9!#e(B

_ e$B$G;O$^$k%7%s%%k$J$i!"$$$+$K$bFC<l$J0UL#$r;}$C$F$$$=$&$Je(B
e$B%*!<%i$,=P$F$k$N$G!"5v$;$k5$$b$7$F$-$^$7$?!#e(B
e$B$$$$2C8:$G$9$_$^$;$s!#e(B

nil, false e$B$OL\N)$D$N$G!"%7%s%%k$@$1$H$$$&$N$O$R$H$D$N0F$+e(B
e$B$b$7$l$^$;$s!#e(B

e$B$?$@!“e(B[ruby-dev:38394] e$B$N$h$&$J4|BT$KEz$($k$J$i!”$`$7$me(B nil,
false e$B$r:o=|$H$9$k$N$O@5$7$$$N$+$b$7$l$J$$!"$H$b;W$$$^$9!#e(B

e$B$=$&$G$9$M!#:o=|$H$$$&0UL#$G$J$/J,N`$N0UL#$Ge(B nil e$B$de(B false
e$B$r;H$&$3$H$O$[$H$s$I$J$5$=$&$G$9$7!#e(B

nil e$B$NFCJL07$$$O$I$&$9$k$K$7$F$b!"e(BPython e$B$Ne(B API
e$B$N$h$&$K!“e(B
e$B%+%F%4%j$r$”$o$;$Fe(B yield e$B$9$k$H$3$m$O??;w$7$FM_$7$$$G$9!#e(B
e$B$G$J$$$H!"e(Bgather e$B8e$K%+%F%4%j$r:F7W;;$9$k%3!<%I$,NL;:$5$le(B
e$B$=$&$G$9!#$3$s$JIw$K!"e(Bcategory e$B$rFs2s8F$S$?$/$J$$$G$9!#e(B

enum.gather {|x| category(x) }.each do |ary|
case category(ary.first)

end
end

e$B@.@%$G$9!#e(B

Yusuke ENDOH wrote:

nil, false e$B$OL\N)$D$N$G!"%7%s%%k$@$1$H$$$&$N$O$R$H$D$N0F$+e(B
e$B$b$7$l$^$;$s!#e(B

e$B$?$@!“e(B[ruby-dev:38394] e$B$N$h$&$J4|BT$KEz$($k$J$i!”$`$7$me(B nil,
false e$B$r:o=|$H$9$k$N$O@5$7$$$N$+$b$7$l$J$$!"$H$b;W$$$^$9!#e(B

e$B$=$&$G$9$M!#:o=|$H$$$&0UL#$G$J$/J,N`$N0UL#$Ge(B nil e$B$de(B false
e$B$r;H$&$3$H$O$[$H$s$I$J$5$=$&$G$9$7!#e(B

nile$B$He(Bfalsee$BN>J}$G$J$/!“JRJ}$K$7$?J}$,$$$$$N$G$O$J$$$G$9$+$M!#e(B
true/falsee$B$J%a%=%C%I$NLa$jCM$GJ,N`$H$$$&$N$O$=$l$J$j$K$”$k$H;W$&$N$G$9$,!#e(B

In article [email protected],
“NARUSE, Yui” [email protected] writes:

nile$B$He(Bfalsee$BN>J}$G$J$/!“JRJ}$K$7$?J}$,$$$$$N$G$O$J$$$G$9$+$M!#e(B
true/falsee$B$J%a%=%C%I$NLa$jCM$GJ,N`$H$$$&$N$O$=$l$J$j$K$”$k$H;W$&$N$G$9$,!#e(B

e$B:#$O!"e(Bnil e$B$He(B false
e$B$r;H$$J,$1$k$N$OLLE]$J$s$8$c$J$$$+$J$!!“e(B
e$B$H;W$C$FF1$807$$$K$7$F$”$j$^$9!#e(B

e$B;H$&$H$-$K!"$I$C$A$,$I$C$A$@$C$?$+;W$$=P$;$J$+$C$?$j$7$^$;$se(B
e$B$+$M!#e(B

e$B$J$*!"$b$7$b$I$A$i$+JRJ}$K$9$k$J$i!"e(Bnil e$B$@$H$O;W$$$^$9!#e(B

In article
[email protected],
Yusuke ENDOH [email protected] writes:

e$B$=$&$$$&%1!<%9$O$h$/$“$k$b$N$J$s$G$7$g$&$+!#e(B
nil e$B$NJ}$O5$L#$,0-$$$b$N$N<{MW$O$”$j$=$&$@$H;W$&$s$G$9$,!“e(B
:_singleton e$B$O$b$C$HIT5$L#$G!”$K$b4X$o$i$:;H$$$I$3$m$,e(B
e$B$"$^$j;W$$$D$-$^$;$s!#e(B

e$B0lHL$K!"0lIt$r=q$-49$($?$$$H$$$&$H$-$K$O>o$KLr$KN)$D$H;W$$$^e(B
e$B$9!#e(B

e$B0lIt$r=q$-49$($?$b$N$rF@$?$$$H$$$&$H$-$K$O!“=q$-49$($?$$$H$3e(B
e$B$m$@$1$G$J$/!”=q$-49$($?$/$J$$$H$3$m$bI,MW$G$9!#$3$l$O=q$-49e(B
e$B$($?$/$J$$$b$N$K$O$=$N$^$^=PNO$9$k$H$$$&=hM}$,I,MW$@$+$i$G$9!#e(B
e$B$=$&$$$&!"6=L#$NBP>]$G$J$$$H$3$m$K<j4V$,$+$+$k$N$O$h$m$7$/$"e(B
e$B$j$^$;$s!#e(B

_ e$B$G;O$^$k%7%s%%k$J$i!"$$$+$K$bFC<l$J0UL#$r;}$C$F$$$=$&$Je(B
e$B%*!<%i$,=P$F$k$N$G!"5v$;$k5$$b$7$F$-$^$7$?!#e(B
e$B$$$$2C8:$G$9$_$^$;$s!#e(B

e$B$=$&$$$&0u>]$O<B:]$K1F6A$r;}$C$F$$$k$H;W$$$^$9!#5!G=$NM-L5$@e(B
e$B$1$G$J$/!"$=$&$$$&B&LL$b5DO@$G$-$k$H$$$$$G$9$M!#e(B

In article [email protected],
Yukihiro M. [email protected] writes:

e$B$9$G$K=P$F$$$?e(Bslice_bye$B$H$+$,$$$$$h$&$J5$$b$7$^$9!#e(B

e$B$,!"EDCf$5$s$,Ds0F$7$F$$$k5!G=A4Ite(B(e$BMWAG:o=|$H$+e(B)e$B$Oe(Bslice_bye$B$Ge(B
e$B$OI=8=$7$-$l$J$$$h$&$J5$$b$7$^$9$,!#e(B

e$B$Ge(B slice e$B$r$R$/$H!"e(B

  1.  to cut off or remove as a slice or slices (sometimes fol. by 
    

off, away, from, etc.).

e$B$H$"$k$N$G!“e(Bslice e$B$b:o=|$N0UL#$K;H$&$3$H$O$”$k$h$&$G$9!#e(B