2つのリストlistOfA
とlistOfB
を作成して、別のリストからのA
とB
のインデックスを保存します。
s=['A','B','A','A','A','B','B']
出力は2つのリストである必要があります
listOfA=[0,2,3,4]
listOfB=[1,5,6]
2つのステートメントでこれを行うことができます。
listOfA=[idx for idx,x in enumerate(s) if x=='A']
listOfB=[idx for idx,x in enumerate(s) if x=='B']
ただし、リスト内包表記のみを使用して、1回の反復でそれを実行する必要があります。単一のステートメントでそれを行うことは可能ですか?何かのようなもの listOfA,listOfB=[--code goes here--]
リスト内包のまさに定義は、oneリストオブジェクトを生成することです。 2つのリストオブジェクトは長さが異なります。あなたはあなたが望むものを達成するために副作用を使わなければならないでしょう。
ここではリスト内包表記を使用しないでください。通常のループを使用するだけです。
_listOfA, listOfB = [], []
for idx, x in enumerate(s):
target = listOfA if x == 'A' else listOfB
target.append(idx)
_
これにより、実行するoneループだけが残ります。これは、少なくとも2つのリスト内包表記に勝るでしょう。少なくとも、開発者がリスト内包表記に、個別のlist.append()
呼び出しを使用したループの2倍の速さでリストを作成させる方法を見つけるまではです。
入れ子になったリスト内包表記justよりもいつでもこれを選択して、1行に2つのリストを作成できるようにします。 Zen of Python の状態:
読みやすさが重要です。
ソート;重要なのは、展開できる2要素のリストを生成することです。
listOfA, listOfB = [[idx for idx, x in enumerate(s) if x == c] for c in 'AB']
そうは言っても、そのようにするのはかなりお手上げだと思います。明示的なループははるかに読みやすくなっています。
この問題への素晴らしいアプローチはdefaultdictを使用することです。 @Martinがすでに述べたように、リスト内包は2つのリストを作成するための適切なツールではありません。 defaultdictを使用すると、単一の反復を使用して分離を作成できます。さらに、コードはどのような形でも制限されません。
>>> from collections import defaultdict
>>> s=['A','B','A','A','A','B','B']
>>> listOf = defaultdict(list)
>>> for idx, elem in enumerate(s):
listOf[elem].append(idx)
>>> listOf['A'], listOf['B']
([0, 2, 3, 4], [1, 5, 6])
あなたがやろうとしていることは完全に不可能ではありません、それはただ複雑で、おそらく無駄です。
イテラブルを2つのイテラブルに分割する場合、ソースがリストまたはその他の再利用可能なイテラブルである場合、質問のように、2つのパスで実行する方がよいでしょう。
ソースがイテレータであっても、必要な出力が遅延イテレータのペアではなくリストのペアである場合は、 Martijn's answer を使用するか、list(iterator)
を2回パスします。 。)
しかし、本当に任意のイテラブルを2つのイテラブルに遅延分割する必要がある場合、なんらかの中間ストレージなしにそれを行う方法はありません。
_[1, 2, -1, 3, 4, -2]
_をpositives
とnegatives
に分割するとします。次に、next(negatives)
を実行します。それはあなたに_-1
_を与えるはずですよね?しかし、_1
_と_2
_を消費せずにそれを行うことはできません。つまり、next(positives)
を実行しようとすると、_3
_ではなく_1
_が取得されます。したがって、_1
_および_2
_はどこかに保存する必要があります。
あなたが必要とする賢さのほとんどは _itertools.tee
_ の中にラップされています。 positives
とnegatives
を同じイテレータの2つのteedコピーにして、両方をフィルタリングすれば完了です。
実際、これはitertools
ドキュメントのレシピの1つです。
_def partition(pred, iterable):
'Use a predicate to partition entries into false entries and true entries'
# partition(is_odd, range(10)) --> 0 2 4 6 8 and 1 3 5 7 9
t1, t2 = tee(iterable)
return filterfalse(pred, t1), filter(pred, t2)
_
(それが理解できない場合は、2つのジェネレーター関数がクロージャーを介してイテレーターとT型を共有するか、クラスの2つのメソッドがself
を介してそれらを共有するかのいずれかで、おそらく明示的に書き出す価値があります。数十行になるはずです。トリッキーなものを必要としないコードの例)
また、_more_itertools
_などのサードパーティライブラリからのインポートとしてpartition
を取得することもできます。
これで、これをワンライナーで使用できます。
_lst = [1, 2, -1, 3, 4, -2]
positives, negatives = partition(lst, lambda x: x>=0)
_
…そして、すべての正の値に対するイテレータと、すべての負の値に対するイテレータがあります。それらは完全に独立しているように見えますが、一緒にlst
を1回渡すだけです。そのため、lst
をリストではなくジェネレーター式またはファイルなどに割り当てても機能します。
では、なぜこれに何らかのショートカット構文がないのですか?かなり誤解を招くからです。
理解は余分なストレージを必要としません。これが、ジェネレータ式が非常に優れている理由です。ジェネレータ式は、何も保存せずに遅延イテレータを別の遅延イテレータに変換できます。
ただし、これにはO(N)
ストレージが必要です。すべての数値が正であると想像してください。最初にnegative
を反復しようとします。何が起こるのですか?すべての数値はtrueq
にプッシュされます。実際、そのO(N)
はinfiniteである可能性もあります(たとえば、itertools.count()
で試してください)。
これは_itertools.tee
_のようなもので問題ありません。ほとんどの初心者は知らないモジュールにスタックされた関数であり、その機能を説明してコストを明確にすることができる素敵なドキュメントがあります。しかし、それを通常の理解のように見える構文糖でそれを行うと、別の話になります。
エッジに住んでいる人のために;)
listOfA, listOfB = [[i for i in cur_list if i is not None] for cur_list in Zip(*[(idx,None) if value == 'A' else (None,idx) for idx,value in enumerate(s)])]