リストでも配列でもいいけど、つまりこういうのを
[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]] []
これでいいだろう。
「リスト(配列)の中で隣り合う同じ値をグループ化する」への1件のフィードバック