リストでも配列でもいいけど、つまりこういうのを
[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]]
[]
これでいいだろう。