web-dev-qa-db-ja.com

シーケンスのシーケンスをフラット化するための理解?

シーケンスのシーケンス(おそらくタプルのリスト)がある場合は、itertools.chain()を使用してそれをフラット化できます。でも時々、理解力として書きたいと思うことがあります。私はそれを行う方法を理解することができません。これは非常に解釈されたケースです:

シーケンス内のすべてのペアの要素を交換したいとします。ここでは、文字列をシーケンスとして使用します。

>>> from itertools import chain
>>> seq = '012345'
>>> swapped_pairs = Zip(seq[1::2], seq[::2])
>>> swapped_pairs
[('1', '0'), ('3', '2'), ('5', '4')]
>>> "".join(chain(*swapped_pairs))
'103254'

シーケンスの偶数スライスと奇数スライスでZipを使用して、ペアを交換します。しかし、フラット化する必要のあるタプルのリストができあがります。だから私はchain()を使います。代わりに理解して表現する方法はありますか?

ペアの要素を交換するという基本的な問題に対する独自の解決策を投稿したい場合は、先に進んでください。私に何か新しいことを教えてくれるものは何でも投票します。ただし、「いいえ、できません」という回答であっても、自分の質問を対象とした回答のみを承認済みとしてマークします。

37
PEZ

理解して?上手...

>>> seq = '012345'
>>> swapped_pairs = Zip(seq[1::2], seq[::2])
>>> ''.join(item for pair in swapped_pairs for item in pair)
'103254'
31
nosklo

私が見つけた最も速いのは、空の配列から始めてそれを拡張することです:

_In [1]: a = [['abc', 'def'], ['ghi'],['xzy']]

In [2]: result = []

In [3]: extend = result.extend

In [4]: for l in a:
   ...:     extend(l)
   ...: 

In [5]: result
Out[5]: ['abc', 'def', 'ghi', 'xzy']
_

これは、Alex Martelliが試みた例の2倍以上の速さです: Pythonのリストのリストからフラットリストを作成する

_$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' '[item for sublist in l for item in sublist]'
10000 loops, best of 3: 86.3 usec per loop

$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99'  'b = []' 'extend = b.extend' 'for sub in l:' '    extend(sub)'
10000 loops, best of 3: 36.6 usec per loop
_

これを思いついたのは、舞台裏で、extendがリストに適切な量のメモリを割り当て、おそらくいくつかの低レベルのコードを使用してアイテムを移動するという予感があったからです。これが本当かどうかはわかりませんが、誰が気にするか、それはより速いです。

ちなみに、これは直線的なスピードアップにすぎません。

_$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]'  'b = []' 'extend = b.extend' 'for sub in l:' '    extend(sub)'
1000000 loops, best of 3: 0.844 usec per loop

$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]' '[item for sublist in l for item in sublist]'
1000000 loops, best of 3: 1.56 usec per loop
_

map(results.extend, a)を使用することもできますが、Noneの独自のリストを作成しているため、これは遅くなります。

また、関数型プログラミングを使用しないことの利点のいくつかを提供します。つまり.

  • 空のリストを作成する代わりに、既存のリストを拡張できます。
  • 数分、数日、さらには数か月後でも、コードを一目で理解できます。

ちなみに、リスト内包を避けるのがおそらく最善です。小さいものはそれほど悪くはありませんが、一般的にリスト内包表記は実際には多くの入力を節約しませんが、理解するのが難しく、変更やリファクタリングが非常に難しいことがよくあります(3レベルのリスト内包表記を見たことがありますか?)。 Googleのコーディングガイドラインは、単純な場合を除いて、それらに対してアドバイスしています。 私の意見では、これらは「使い捨て」コード、つまり、作成者が読みやすさを気にしないコード、または将来のメンテナンスを必要としないことが知られています。

同じことを書くこれらの2つの方法を比較してください:

_result = [item for sublist in l for item in sublist]
_

これとともに:

_result = []
for sublist in l:
    for item in sublist:
        result.append(item)
_

YMMV、しかし最初のものは私のトラックで私を止めて、私はそれについて考えなければなりませんでした。 2番目の例では、インデントからネストが明らかになります。

16
Mike A

目標を達成するためにreduceを使用できます。

In [6]: import operator
In [7]: a = [(1, 2), (2,3), (4,5)]
In [8]: reduce(operator.add, a, ())
Out[8]: (1, 2, 2, 3, 4, 5)

元のリストの要素は連結されるタプルであるため、これはリストではなくタプルを返します。ただし、そこからリストを簡単に作成でき、joinメソッドはタプルも受け入れます。

ちなみに、リスト内包表記はそのための適切なツールではありません。基本的に、リスト内包表記は、このリストの要素がどのように見えるかを記述することによって、新しいリストを作成します。要素のリストを1つの値だけに減らしたいとします。

3
unbeknown
>>> a = [(1, 2), (3, 4), (5, 6)]
>>> reduce(Tuple.__add__, a)
>>> (1, 2, 3, 4, 5, 6)

または、内部シーケンスのタイプにとらわれないようにする(すべて同じである限り):

>>> reduce(a[0].__class__.__add__, a)
1
Arkady