Pythonで数値とその負の値のリストを生成する


32

Pythonで数値とその負の値のリストを生成する便利なワンライナーはありますか?

たとえば、6から9および-6から-9の番号のリストを生成したいとします。

私の現在のアプローチは:

l = [x for x in range(6,10)]
l += [-x for x in l]

単純な「ワンライナー」は次のようになります。

l = [x for x in range(6,10)] + [y for y in range(-9, -5)]

ただし、2つのリストを生成してからそれらを結合するのは不便に思えます。


3
正の数は負の数の前に来るべきですか?
エーリヒ

2
@Erichいいえ、私の場合、順序は関係ありません。
UPE

6
順序が重要ではない場合、それはリストである必要さえありますか?セットは(順序付けされていない)OKですか、それともジェネレータまたはタプル(どちらも順序付けされています)ですか?
JG

@JG後で図を作成したいかもしれません。おそらくスキャッターなどを使用しているscalar or array-like, shapeと思われます。
UPE

2
これらの答えのほとんどは、いくつかのゴルフ対策ソリューションのように読んでいます。ソート、ジェネレーター関数、itertools。私はあなたが提供したコードをほとんどの「答え」よりも維持したいと思っています。
Peilonrayz

回答:


45

順序が重要かどうかはわかりませんが、タプルを作成してリスト内包に展開できます。

nums = [y for x in range(6,10) for y in (x,-x)]
print(nums)
[6, -6, 7, -7, 8, -8, 9, -9]

52

見やすくて読みやすい関数を作成します。

def range_with_negatives(start, end):
    for x in range(start, end):
        yield x
        yield -x

使用法:

list(range_with_negatives(6, 10))

これは、何にでも便利なワンライナーを取得する方法です。魔法のプロのハッカーのように見えないようにしてください。


21
リスト/辞書の内包表記に対する偏見はSOでかなり一般的であり、通常、それらが「読み取り可能」ではないと言うことで正当化されます。構成の固有の可読性(使用方法ではなく)は主に主観的です。個人的には理解度読みやすくなり、この解決策ははるかに少なくなります(比較:「50歳以上のすべての男性の血圧を書き留めてください」対「すべての人を通過します。OK、彼らは男性ですか?もしそうなら...」 )。私が何かを「見た目」にしようとしているのではなく、この理由でそれらを使用しています。長い理解は、それが問題である場合、複数の行に分かれることがあります。
jez

2
けっこうだ。しかし、ここで主観が出てきます。私は、個人的に、リスト機能の解決策を、ジェネレーター関数と同じくらい、精神的緊張の観点から読めるようにしています。ただし、名前yをに変更することで、Datanoviceの回答の読みやすさ(つまり、私にとっての読みやすさ)を向上させsignedValueます。関数を定義するアプローチの真の付加価値は、chainわずかに異なる数値引数が持つバーマーのワンライナーの問題を回避しながら、質問が要求する厳密な順序で結果を提供することです(ポジティブ、次にネガティブ)。2回ハードコード化する。
jez

2
リスト内包表記に関する一般的な意見には同意しますが、このような関数を定義することも、知っておくと非常に便利なテクニックです。(再帰的なジェネレーターを作成し、最上位の結果をlist何にでも渡すことにより、再帰的なアルゴリズムから結果を収集することは特に強力です。)これらの決定を行う最善の方法を体系化しようと長年試みた後、私にとって意味のある唯一の一般原則:プログラムで何かに付けるべき明白で適切な名前がある場合は、その機会を利用してください(関数はこれを行う方法の1つです)。
Karl Knechtel

43

最も簡単な解決策は、*アンパック演算子を使用して2つの範囲をリストにアンパックすることです。

>>> [*range(6, 10), *range(-9, -5)]
[6, 7, 8, 9, -9, -8, -7, -6]

これは、提案されている最も短い回答であるだけでなく、単一のリストを作成するだけで、2つを超える関数呼び出しを含まないため、最もパフォーマンスが高くなりますrange

timeitモジュールを使用してこの質問のすべての回答をテストすることで、これを確認しました。

回答IDメソッドtimeit結果
-------------------------------------------------- ------------------------------------------------
(問題)[x for x in range(6,10)] + [y for y for range(-9、-5)] 0.843 usec per loop
(この答え)[* range(6、10)、* range(-9、-5)]ループあたり0.509 usec
61348876 [y in(x、-x)のy(範囲(6,10)のyの場合]]ループあたり0.754 usec
61349149 list(range_with_negatives(6、10))0.795 usec /ループ
61348914 list(itertools.chain(range(6、10)、range(-9、-5)))0.709 usec per loop
61366995 [sign * x for sign、x in itertools.product((-1、1)、range(6、10))] 0.899 usec per loop
61371302 list(range(6、10))+ list(range(-9、-5))0.729ループあたりのusec
61367180 list(range_with_negs(6、10))1.95ループあたりのusec

(自分のコンピューターでPython 3.6.9を使用して実行した時限テスト(平均仕様))


2
アイテムが10個未満の例である場合、パフォーマンスが適切であると想定することにあまり熱心ではありませんが、これが明らかに最も簡単なソリューションです。
JollyJoker

負の値のハードコーディングをせずに、その開始と終了をどのようにして把握しますか?
アルドッカニ

1
@aldokkani[*range(x, y), *range(-y + 1, -x + 1)]
RoadrunnerWMC

20

を使用itertools.chain()して、2つの範囲を連結できます。

import itertools
list(itertools.chain(range(6, 10), range(-9, -5)))

1
順序を明確にするために 'range(-6、-10、-1)'を使用します(6と10を変数に置き換えます)
MaartenFabréApr

7

itertools.productデカルト積であるを使用できます。

[sign*x for sign, x in product((-1, 1), range(6, 10))]
[-6, -7, -8, -9, 6, 7, 8, 9]

乗算を使用するためこれは遅くなるかもしれませんが、読みやすいはずです。

あなたは純粋に機能的なソリューションをしたい場合は、インポートすることができますitertools.starmapoperator.mul

from itertools import product, starmap
from operator import mul

list(starmap(mul, product((-1, 1), range(6, 10))))

ただし、これは読みにくくなります。


5
ネストされたリスト内包表記と比較して、、、および不必要に鈍角の使用を見つけましたがproduct、乗算を使用することの提案を承認します。は、よりもわずか10%遅く、順序が重要な場合は、タプルベースのアプローチのソートより3倍以上高速です。starmapopertaor.mul[x * sign for sign in (1, -1) for x in range(6, 10)][y for x in range(6, 10) for y in (x, -x)]
DarknessFishへの接近

これは素晴らしいアイデアですが、おそらく問題の詳細に少し具体的すぎます。他の人が提供する手法はより一般的に適用されます。
Karl Knechtel

@ApproachingDarknessFish同意しstarmapmul回避する必要がproductありますが、イテレータとその要素を別々にグループ化するので、読みやすくなります。リスト内包表記の二重ループも、予期しない順序のために混乱する可能性があります。
フランクヴェル

4

2つのrangeオブジェクトを組み合わせて、あなたは本当に近くにいます。しかし、それを行う簡単な方法があります。

>>> list(range(6, 10)) + list(range(-9, -5))
[6, 7, 8, 9, -9, -8, -7, -6]

つまり、各rangeオブジェクトをリストに変換してから、2つのリストを連結します。

itertoolsを使用した別のアプローチ:

>>> list(itertools.chain(range(6, 10), range(-9, -5)))
[6, 7, 8, 9, -9, -8, -7, -6]

itertools.chain()一般化のようなものである+代わりに、二つのリストを追加することで、それはチェーン「スーパーイテレータを」作るために次々イテレータ:。次に、それを渡すとlist()、メモリに必要なすべての数値を含む具体的なリストが表示されます。


3
この答えは、[x for x in ...]より正確に記述された洞察に対してのみ価値がありますlist(...)
Karl Knechtel

2

さらに別の可能性を検討します。

可読性が必要な場合は、元のワンライナーはかなり良かったのですが、範囲を変更して、負の境界によって物事がわかりにくくなると思います。

[x for x in range(6, 10)] + [-x for x in range(6, 10)]

2

itertools.chain他のいくつかの回答で提示されているIMOを使用したアプローチは、これまでに提供された方法の中で間違いなく最もクリーンです。

ただし、値の順序は重要はないので、2つの明示的なrangeオブジェクトを定義する必要がなく、次のように使用することで、負のrangeインデックス付けに必要な1つずつの計算をすべて回避できますitertools.chain.from_iterable

>>> import itertools
>>> list(itertools.chain.from_iterable((x, -x) for x in range(6, 10)))
[6, -6, 7, -7, 8, -8, 9, -9]

少し冗長ですが、十分に読みやすいです。

別の同様のオプションは、プレーンでタプル/引数のアンパックを使用することですchain

>>> list(itertools.chain(*((x, -x) for x in range(6, 10))))
[6, -6, 7, -7, 8, -8, 9, -9]

より簡潔ですが、クイックスキャンではタプルの解凍が難しくなります。


2

これは、テーマのバリエーションです(@Derte Trdelnik回答を参照してください)itertools

イテレータのビルディングブロック[...]は、単独でも組み合わせても便利です。

アイデアは、新しい関数を定義している間に、それを汎用にすることも考えられるということです。

def interleaved_negatives(it):
    for i in it:
        yield i
        yield -i

そしてそれを特定のrangeイテレータに適用します:

list(interleaved_negatives(range(6, 10)))

0

指定した順序を維持したい場合は、Pythonの組み込みの範囲ジェネレーターを条件付きで使用できます。

def range_with_negs(start, stop):
    for value in range(-(stop-1), stop):      
        if (value <= -start) or (value >= start):
            yield value

これはあなたに出力を与えます:

In [1]: list(range_with_negs(6, 10))
Out[1]: [-9, -8, -7, -6, 6, 7, 8, 9]

また、全範囲の開始として0を使用します。


2
これは、の大きな値に対しては非常に非効率的ですstart
ソロモンウッコ

0

仕事を成し遂げるためのさまざまな方法があり得る。

指定された変数:1. start = 6 2. stop = 10

別のアプローチでこれを試すこともできます:

def mirror_numbers(start,stop):
  if start<stop:
    val=range(start,stop)
    return [j if i < len(val) else -j for i,j in enumerate([x for x in val]*2) ]

mirror_numbers(6,10)

-1

l対称のように。

a = 6 b = 10

nums = [範囲内のyの(-(a + b-1)、0)でのxのx + y(a、b)]

結果は-9、-8、...、8、9になります。

numsの表現は改善できると思います。「in」と「range」に続くものは、まだバランスが取れていません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.