Python には組み込みの flatten がない。そこで次のように実装した。
def traverse(f, x): if isinstance(x, (list, tuple)): for e in x: traverse(f, e) else: f(x) def flatten(listOfList): arr = [] traverse(arr.append, listOfList) return arr
「Python flatten」でググると様々な実装例が見つかるのだが、妙に凝った実装が多くて、こういうシンプルな実装はあまり見当たらなかった。
ただしこの実装は再帰を使っているので、ネストが深すぎるとオーバーフローする。
x = [] for i in xrange(1000): x = [x, i] print flatten(x) # => RuntimeError: maximum recursion depth exceeded
オーバーフローを避けるには traverse をループを使って書き下す。Pythonでは末尾再帰の最適化は行われない。
def traverse(f, x, isSeq = lambda y: isinstance(y, (list, tuple))): if not isSeq(x): return f(x) pos = depth = 0 stack = { depth: [x, pos, len(x)] } while depth >= 0: lis, pos, size = stack[depth] if pos < size: stack[depth][1] = pos + 1 if isSeq(lis[pos]): depth += 1 stack[depth] = [lis[pos], 0, len(lis[pos])] else: f(lis[pos]) else: depth -= 1
これで100000とかネストしても平気。
x = [] for i in xrange(100000): x = [x, i] print flatten(x) # => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...省略... , 99977, 99978, 99979, 99980, 99981, 99982, 99983, 99984, 99985, 99986, 99987, 99988, 99989, 99990, 99991, 99992, 99993, 99994, 99995, 99996, 99997, 99998, 99999]