例えば $\bmod 3$だったら
ns = range(30)
m = 3
list(zip(*zip(*[iter(ns)]*m)))
[(0, 3, 6, 9, 12, 15, 18, 21, 24, 27),
(1, 4, 7, 10, 13, 16, 19, 22, 25, 28),
(2, 5, 8, 11, 14, 17, 20, 23, 26, 29)]
となり,$3$ で割ったときの余りが $0$ のもの,$1$ のもの,$2$ のもので整数を分けることができます.数でないリストに突っ込んだ場合,中身を3つ飛ばしにしたものを1つの固まりにします.
ls = list('abcdefghijkl')
m = 3
list(zip(*zip(*[iter(ls)]*m)))
[('a', 'd', 'g', 'j'), ('b', 'e', 'h', 'k'), ('c', 'f', 'i', 'l')]
この記法がクールと思えるかどうかは分かりませんが,なぜこれでうまくいくかを簡単に説明しましょう.例えば
s = list(range(12))
s
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
を3つの数ずつに4分割するのに
list(zip(*[iter(s)]*3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11)]
という方法があるのは割と有名だと思います.実は,公式チュートリアルのzip関数のところにいきなり書いてあります.
zip(*[iter(s)]*n) を使ってデータ系列を長さ n のグループにクラスタリングするイディオムが使えます。
よくよく眺めていると,この結果をzip
関数に突っ込めば,3つ飛ばしのリストが出来上がることに気付くと思います.
list(zip(*zip(*[iter(s)]*3)))
[(0, 3, 6, 9), (1, 4, 7, 10), (2, 5, 8, 11)]
但し,この方法だと,10個の数のリストを3つの数ずつに分割しようとすると,半端な部分が切り捨てられてしまいます.
list(zip(*[iter(list(range(10)))]*3)) #9が切り捨てられる
[(0, 1, 2), (3, 4, 5), (6, 7, 8)]
これはzip
関数の仕様です.これを回避するために,itertools
にzip_longest
関数が用意されています.
from itertools import zip_longest
list(zip_longest(*[iter(list(range(10)))]*3)) #半端なところにはNoneが入る
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, None, None)]
これを用いて,剰余類の方もアップデートしましょう.最後にNone
を取り除く行程があるのですが,tuple
がイミュータブルである点は注意です.最終的には数学らしく,set
の集まりにしました.
from itertools import zip_longest
ns = range(50)
m = 3
[{x for x in t if x != None} for t in zip(*zip_longest(*[iter(ns)]*m))]
[{0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48},
{1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, 49},
{2, 5, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35, 38, 41, 44, 47}]
zip
関数は名前があまり好きじゃないのですが,奥が深いです.