たとえば
ary = [:a, :b, :c]
が与えられたときに
[
[:a, :b],
[:b, :c],
[:c, :a]
]
とか
[
[:a, :b, :c],
[:b, :c, :a],
[:c, :a, :b]
]
とかについてループを回したい,ということがよくある。
要するに,与えられた配列のお尻と頭が繋がった円環状の構造についての each_cons
だ。
each_cons
の cyclic 版とでも言おうか。
これは,以下のように書ける。組み込みクラスの Array を拡張しちゃうので,refinement を使ってお行儀よくいこう。
module CycleEachConsToArray
refine Array do
def cycle_each_cons(n)
return Enumerator.new{ cycle_each_cons(n, &_1) } unless block_given?
cycle.each_cons(n).take(length).each{ yield _1 }
end
end
end
これを使うときは,使いたい場所で
using CycleEachConsToArray
してから
p %w[A B C].cycle_each_cons(2).to_a
# => [["A", "B"], ["B", "C"], ["C", "A"]]
%w[A B C D].cycle_each_cons(3) do |ary|
puts ary.join("-")
end
# => A-B-C
# B-C-D
# C-D-A
# D-A-B
のようにする。
Array
に限定せず Enumerable
でやれば応用が広がるように思うかもしれないが,長さがあらかじめ分かる必要があるので,一般の Enumerable
についてはできない。
有限の長さを持つ「Array
でない Enumerable
オブジェクト」に使いたいときは to_a
してから使えばいい。