必要に応じて前後の要素にもアクセスできる each
こういうのはまだ出ていないみたいなので…。与えたブロック引数の個数をみて適当に割り振ります。
class Array def ya_each(&block) n_args = block.arity.abs mid = n_args / 2 ds = (0...n_args).map{ |i| i - mid } each_index do |i| yield(*ds.map{ |d| self[i + d] }) end end end
(1..10).to_a.ya_each{ |e| p e }
=> 1 2 3 4 5 6 7 8 9 10
(1..10).to_a.ya_each{ |pv,ea| p [pv,ea] }
=> [10, 1] [1, 2] [2, 3] [3, 4] [4, 5] [5, 6] [6, 7] [7, 8] [8, 9] [9, 10]
(1..10).to_a.ya_each{ |pv,ea,su| p [pv,ea,su] }
=> [10, 1, 2] [1, 2, 3] [2, 3, 4] [3, 4, 5] [4, 5, 6] [5, 6, 7] [6, 7, 8] [7, 8, 9] [8, 9, 10] [9, 10, nil]
(1..10).to_a.ya_each{ |pv2,pv1,ea,su1,su2| p [pv2,pv1,ea,su1,su2] }
=> [9, 10, 1, 2, 3] [10, 1, 2, 3, 4] [1, 2, 3, 4, 5] [2, 3, 4, 5, 6] [3, 4, 5, 6, 7] [4, 5, 6, 7, 8] [5, 6, 7, 8, 9] [6, 7, 8, 9, 10] [7, 8, 9, 10, nil] [8, 9, 10, nil, nil]
ちなみに Smalltalk(Ruby の #each に当たるのは #do:)だとこんな感じ。
SequenceableCollection>>yetAnotherDo: doBlock | numArgs middleIndex deltas | numArgs := doBlock numArgs. middleIndex := numArgs // 2 + 1. deltas := (1 to: numArgs) collect: [:idx | idx - middleIndex]. 1 to: self size do: [:idx | doBlock valueWithArguments: (deltas collect: [:dlt | self atWrap: idx + dlt])]
World findATranscript: nil. Transcript cr. (1 to: 9) do: [:each | Transcript space; show: each]. "=> 1 2 3 4 5 6 7 8 9 " Transcript cr. (1 to: 9) yetAnotherDo: [:each | Transcript space; show: each]. "=> 1 2 3 4 5 6 7 8 9 " Transcript cr. (1 to: 9) yetAnotherDo: [:prev :each | Transcript space; show: {prev. each}]. "=> #(9 1) #(1 2) #(2 3) #(3 4) #(4 5) #(5 6) #(6 7) #(7 8) #(8 9) " Transcript cr. (1 to: 9) yetAnotherDo: [:prev :each :next | Transcript space; show: {prev. each. next}] "=> #(9 1 2) #(1 2 3) #(2 3 4) #(3 4 5) #(4 5 6) #(5 6 7) #(6 7 8) #(7 8 9) #(8 9 1) "
さらに Smalltalk では擬変数「thisContext」を使ってブロックが評価される際のコンテキストをたぐることで、実用性はともかくいろいろと遊べます。
(1 to: 10) do: [:each | | rcvr idx | rcvr := thisContext sender receiver. idx := thisContext sender tempAt: 2. Transcript cr; show: {rcvr atWrap: idx - 1. each. rcvr atWrap: idx + 1}]
=> #(10 1 2) #(1 2 3) #(2 3 4) #(3 4 5) #(4 5 6) #(5 6 7) #(6 7 8) #(7 8 9) #(8 9 10) #(9 10 1)
(ブロック引数が可変長のときに arity が負数を返すのに対応するための abs の追加と、Smalltalk での #yetAnotherDo: の例を追記しました。del.icio.us でのご指摘を受けて、Array に変えました。)