Listeyi anlama: Her bir öğe için iki (veya daha fazla) öğe döndürme


92

Bir liste anlayışındaki her bir öğe için 2 (veya daha fazla) öğe döndürmek mümkün müdür?

Ne istiyorum (örnek):

[f(x), g(x) for x in range(n)]

dönmeli [f(0), g(0), f(1), g(1), ..., f(n-1), g(n-1)]

Yani, bu kod bloğunun yerini alacak bir şey:

result = list()
for x in range(n):
    result.add(f(x))
    result.add(g(x))

3
Meraktan neden bunu yapmak istiyorsun? Bu şekilde yapmaya çalışmadan nihai hedefinize ulaşmanın daha iyi bir yolu olabilir.
murgatroid99 08

4
Temelde işlevsel programlamayı sevdiğim için. Pyglet.graphics.draw işleviyle kullanmak için bir koordinat listesi ekran koordinatlarına eşlemek istiyorum.
Hashmush

Yanıtlar:


54
>>> from itertools import chain
>>> f = lambda x: x + 2
>>> g = lambda x: x ** 2
>>> list(chain.from_iterable((f(x), g(x)) for x in range(3)))
[2, 0, 3, 1, 4, 4]

Zamanlamalar:

from timeit import timeit

f = lambda x: x + 2
g = lambda x: x ** 2

def fg(x):
    yield f(x)
    yield g(x)

print timeit(stmt='list(chain.from_iterable((f(x), g(x)) for x in range(3)))',
             setup='gc.enable(); from itertools import chain; f = lambda x: x + 2; g = lambda x: x ** 2')

print timeit(stmt='list(chain.from_iterable(fg(x) for x in range(3)))',
             setup='gc.enable(); from itertools import chain; from __main__ import fg; f = lambda x: x + 2; g = lambda x: x ** 2')

print timeit(stmt='[func(x) for x in range(3) for func in (f, g)]',
             setup='gc.enable(); f = lambda x: x + 2; g = lambda x: x ** 2')


print timeit(stmt='list(chain.from_iterable((f(x), g(x)) for x in xrange(10**6)))',
             setup='gc.enable(); from itertools import chain; f = lambda x: x + 2; g = lambda x: x ** 2',
             number=20)

print timeit(stmt='list(chain.from_iterable(fg(x) for x in xrange(10**6)))',
             setup='gc.enable(); from itertools import chain; from __main__ import fg; f = lambda x: x + 2; g = lambda x: x ** 2',
             number=20)

print timeit(stmt='[func(x) for x in xrange(10**6) for func in (f, g)]',
             setup='gc.enable(); f = lambda x: x + 2; g = lambda x: x ** 2',
             number=20)

2.69210777094

3.13900787874

1,62461071932

25.5944058287

29.2623711793

25.7211849286


4
Bu kod gereksiz kayıtlar oluşturur (f(x), g(x)). Daha iyi olarak yazılabilir Could: def fg(x): yield x + 2; yield x ** 2; list(chain.from_iterable(fg(x) for x in range(3))).
khachik

1
Hatta ile genelleştirebilirsiniz chain.from_iterable((func(x) for func in funcs) for x in range(n))). Khachik'in şikayetini tesadüfen ortadan kaldıracak. (Bir anlamda benimki ve onunki süreç açısından esasen aynı.
İçerdeki

Bu, cevabımdan daha iyi sum(..., [])çünkü listeyi her + 'da yeniden oluşturmayı gerektirmiyor (dolayısıyla O (N ^ 2) performansından ziyade O (N) performansı var). sum(..., [])Hızlı bir tek satırlık istediğimde veya acelem olduğunda veya birleştirilen terim sayısı sınırlı olduğunda (örneğin <= 10) yine de kullanacağım .
ninjagecko

@khachik Bunun daha hızlı olacağını düşünüyorum ama şimdi her iki yöntemi de zamanlayacağım, tuples python'da çok hızlı üretiliyor.
jamylak

3
Ortadan kaybolan üçüncü bir cevap şuna benziyordu: [y for x in range(n) for y in (f(x), g(x))]Ama bu muhtemelen daha yavaştır. @jamylak İsterseniz bunu da test edebilirsiniz.
Hashmush

122

Çift liste anlayışı:

[f(x) for x in range(5) for f in (f1,f2)]

Demo:

>>> f1 = lambda x: x
>>> f2 = lambda x: 10*x

>>> [f(x) for x in range(5) for f in (f1,f2)]
[0, 0, 1, 10, 2, 20, 3, 30, 4, 40]

12
Bu güzel çünkü çift liste kompozisyonlarının çok korkutucu olmadığını gösteriyor: sadece döngüler için olduğu gibi yazılan döngüler için iç içe geçmişler . for x in range(5): for f in (f1, f2): newlist.append(f(x)). Sırayı tersine çevirmeye çalıştığım için onları biraz kafa karıştırıcı buluyordum.
DSM

2
Bu kabul edilen cevap olmalı, teşekkürler, harika!
Wingjam

@DSM Bence sonsuza kadar kafa karıştırıcı olacak.)
Winand

11
sum( ([f(x),g(x)] for x in range(n)), [] )

Bu eşdeğerdir [f(1),g(1)] + [f(2),g(2)] + [f(3),g(3)] + ...

Bunu şu şekilde de düşünebilirsiniz:

def flatten(list):
    ...

flatten( [f(x),g(x)] for x in ... )

not: Doğru yol, itertools.chain.from_iterableçift ​​liste anlamayı kullanmaktır. (Listeyi her + 'da yeniden oluşturmayı gerektirmez, dolayısıyla O (N ^ 2) performansı yerine O (N) performansa sahiptir.) sum(..., [])Hızlı bir tek satırlık istediğimde veya acelem olduğunda yine de kullanacağım veya birleştirilen terimlerin sayısı sınırlı olduğunda (örneğin <= 10). Bu nedenle, bu uyarı ile hala burada söz ediyorum. Ayrıca tupleları da kullanabilirsiniz: ((f(x),g(x)) for ...), ()(veya khachik'in yorumu başına, iki tuple veren bir fg (x) oluşturucuya sahip olmak).


@ArashThr: yapıyor[f(1),g(1)] + [f(2),g(2)] + [f(3),g(3)] + ...
ninjagecko

Tam olarak ne yaptığını açıklayabilir misin?
Rsh

Not: Bu, O (N ^ 2) çalışma süresine sahiptir, bu nedenle büyük listelerde yavaş olabilir.
jamylak

1
@jamylak: evet, bunu yorumlarda cevabınızda da belirtmiştim. =)
ninjagecko

sum()Bu şekilde kötüye kullanmayı bir anti-model olarak görüyorum ve her ne koşulda olursa olsun onu kullanmak için herhangi bir gerekçe görmüyorum. Diğer cevabınızdaki kod daha az yazıyor, bu yüzden "hızlı bir tek satırlık yazı istediğimde veya acelem olduğunda" bahanesi bile gerçekten işe yaramıyor.
Sven Marnach

2

Bu lambda işlevi iki listeyi tek bir listeye sıkıştırır:

zipped = lambda L1, L2: [L[i] 
                         for i in range(min(len(L1), len(L2))) 
                         for L in (L1, L2)]

Misal:

>>> f = [x for x in range(5)]
>>> g = [x*10 for x in range(5)]
>>> zipped(f, g)
[0, 0, 1, 10, 2, 20, 3, 30, 4, 40]

2

OP'nin bir liste anlama çözümü aradığını biliyorum, ancak alternatif bir kullanım önermek istiyorum list.extend().

f = lambda x: x
g = lambda x: 10*x

result = []
extend = result.extend
for x in range(5):
    extend((f(x),g(x)))

bu, çift liste anlama kullanmaktan marjinal olarak daha hızlıdır.

nums = range(100000)

def double_comprehension():
    return [func(x) for x in nums for func in (f,g)]

def list_extend():
    result = []
    extend = result.extend
    for x in nums:
        extend((f(x),g(x)))
    return result

%timeit -n100 double_comprehension()
23.4 ms ± 67 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit -n100 list_extend()
20.5 ms ± 213 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Python sürümü: 3.8.0


0

Kullanan bir çözelti azaltmak :

from functools import reduce

f    = lambda x: f"f({x})" ## Just for example
g    = lambda x: f"g({x})"
data = [1, 2, 3]

reduce(lambda acc, x: acc + [f(x), g(x)], data, [])
# => ['f(1)', 'g(1)', 'f(2)', 'g(2)', 'f(3)', 'g(3)']

Bir liste anlayışı olmasa da, bu, soruna yaklaşmanın işlevsel bir yoludur. Bir listeyi anlama, esasen mapveri üzerinde çalışmanın başka bir yoludur , ancak eşlemenin giriş ve çıkış arasında bire bir olmadığı bu durumda, çıktının reducenasıl üretilebileceğine dair biraz kıpırdama odası sağlar.

Genel olarak, forformun herhangi bir uygulaması:

result = []
for n in some_data:
  result += some_operation()
  ## etc.

(Yani bir liste veya benzer veri yapısı üzerinde bir yan etki oluşturması amaçlanan döngüler için)

Bildirime dayalı bir map/reduce/filteruygulamaya dönüştürülebilir .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.