リストでも配列でもいいけど、つまりこういうのを
[1, 1, 2, 2, 3, 1, 1]
こうしたい。
[[1, 1], [2, 2], [3], [1, 1]]
Ruby でやってみた。
def adjacent_group(ary) result = [] current = nil ary.each do |x| if current.nil? current = x result << [x] elsif x == current result[-1] << x else current = x result << [x] end end result end p adjacent_group([1, 1, 2, 2, 3, 1, 1])
^o^ >ruby adjacent_group.rb [[1, 1], [2, 2], [3], [1, 1]]
おなじく Python で。
def adjacent_group(lis): result = [] current = None for x in lis: if current is None: current = x result.append([x]) elif x == current: result[-1].append(x) else: current = x result.append([x]) return result print(adjacent_group([1, 1, 2, 2, 3, 1, 1]))
^o^ >python adjacent_group.py [[1, 1], [2, 2], [3], [1, 1]]
ちょっとベタだな。もう少しスマートにいかないものか、と考えて Ruby の Array#inject
が使えそうだと思いついた。
def adjacent_group(ary) ary.inject([[]]) do |a, x| if a[-1].empty? || x == a[-1][-1] a[-1] << x else a << [x] end a end end p adjacent_group([1, 1, 2, 2, 3, 1, 1])
^o^ >ruby adjacent_group2.rb [[1, 1], [2, 2], [3], [1, 1]]
うまくいった。
さて、じゃ、Python ではどうか。reduce
を使えば同じことができると考えたけど、Python の reduce
は初期値がとれない。まあ、それはリストの頭に初期値をつけてやれば済む話ではあるけど、もうひとつ問題がある。Ruby の Array#inject
はブロックをとれるけど、Python の reduce
には関数を渡してやらなきゃいけないので、関数定義がひとつ増えてしまう。一行では書けないので lambda 式は使えない。
というわけで、上のようにベタに書いたほうがまだマシそうだ。何かいい書き方があったら、誰か教えてください。
[追記](9/25)
Ruby の2つ目の実装では、引数に空の配列を渡したときに期待通りに動作しない([[]]
が返ってきてしまう)。そこでちょっと直したのがこれ。
def adjacent_group(ary) ary.inject([]) do |a, x| if !a.empty? && x == a[-1][-1] a[-1] << x else a << [x] end a end end p adjacent_group([1, 1, 2, 2, 3, 1, 1]) p adjacent_group([])
^o^ >ruby adjacent_group.rb [[1, 1], [2, 2], [3], [1, 1]] []
これでいいだろう。