ความเข้าใจในรายการ: ส่งคืนสองรายการ (หรือมากกว่า) สำหรับแต่ละรายการ


92

เป็นไปได้ไหมที่จะส่งคืน 2 รายการ (หรือมากกว่า) สำหรับแต่ละรายการในความเข้าใจรายการ

สิ่งที่ฉันต้องการ (ตัวอย่าง):

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

ควรกลับ [f(0), g(0), f(1), g(1), ..., f(n-1), g(n-1)]

ดังนั้นสิ่งที่จะแทนที่รหัสบล็อกนี้:

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

3
ด้วยความอยากรู้อยากเห็นทำไมคุณถึงต้องการทำเช่นนี้? อาจมีวิธีที่ดีกว่าในการบรรลุเป้าหมายสุดท้ายโดยไม่ต้องพยายามทำด้วยวิธีนี้
murgatroid99

4
ส่วนใหญ่เป็นเพราะฉันชอบการเขียนโปรแกรมเชิงฟังก์ชัน ฉันต้องการแมปรายการพิกัดกับพิกัดหน้าจอทูเพิลเพื่อใช้กับฟังก์ชัน pyglet.graphics.draw
Hashmush

คำตอบ:


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]

การกำหนดเวลา:

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
รหัสนี้สร้าง (f(x), g(x))tuples สามารถเขียนได้ดีกว่า: def fg(x): yield x + 2; yield x ** 2; list(chain.from_iterable(fg(x) for x in range(3))).
khachik

1
chain.from_iterable((func(x) for func in funcs) for x in range(n)))คุณยังสามารถพูดคุยกับ ซึ่งจะช่วยขจัดข้อร้องเรียนของ Khachik โดยบังเอิญ (แม้ว่าในแง่หนึ่งของฉันและของเขาก็เหมือนกันในแง่ของกระบวนการเราเพียงแค่กำหนดเครื่องกำเนิดไฟฟ้าภายในให้แตกต่างกัน)
JAB

นี่ดีกว่าsum(..., [])คำตอบของฉันเพราะไม่จำเป็นต้องสร้างรายการใหม่ในทุกๆ + (ดังนั้นจึงมีประสิทธิภาพ O (N) มากกว่าประสิทธิภาพ O (N ^ 2)) ฉันจะยังคงใช้sum(..., [])เมื่อฉันต้องการหนึ่งซับด่วนหรือฉันกำลังรีบหรือเมื่อจำนวนคำที่รวมกันถูก จำกัด ไว้ (เช่น <= 10)
ninjagecko

@khachik ฉันคิดว่ามันจะเร็วกว่านี้ แต่ฉันจะใช้เวลาทั้งสองวิธีในตอนนี้สิ่งเหล่านี้ถูกสร้างขึ้นอย่างรวดเร็วใน python
jamylak

3
คำตอบที่สามซึ่งหายไปมีลักษณะดังนี้: [y for x in range(n) for y in (f(x), g(x))]แต่อาจช้ากว่านี้ @jamylak คุณสามารถทดสอบสิ่งนี้ได้เช่นกันถ้าคุณต้องการ
Hashmush

122

ความเข้าใจสองรายการ:

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

การสาธิต:

>>> 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
นี้เป็นสิ่งที่ดีเพราะมันแสดงให้เห็นว่า comps รายการคู่ไม่ได้น่ากลัวดังนั้น: พวกเขากำลังซ้อนกันเพียงเพื่อลูปที่เขียนเช่นเดียวสำหรับลูป for x in range(5): for f in (f1, f2): newlist.append(f(x)). ฉันเคยพบว่าพวกเขาสับสนเล็กน้อยเพราะฉันพยายามกลับคำสั่งอยู่เรื่อย ๆ
DSM

2
นี่ควรเป็นคำตอบที่ได้รับการยอมรับขอบคุณมาก!
Wingjam

@DSM ฉันคิดว่ามันจะสับสนตลอดไป)
Winand

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

ซึ่งเทียบเท่ากับ [f(1),g(1)] + [f(2),g(2)] + [f(3),g(3)] + ...

คุณสามารถคิดได้ว่า:

def flatten(list):
    ...

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

หมายเหตุ: วิธีที่ถูกต้องคือการใช้itertools.chain.from_iterableหรือความเข้าใจสองรายการ (ไม่จำเป็นต้องสร้างรายการขึ้นมาใหม่ในทุกๆ + ดังนั้นจึงมีประสิทธิภาพ O (N) มากกว่าประสิทธิภาพ O (N ^ 2)) ฉันจะยังคงใช้sum(..., [])เมื่อฉันต้องการซับหนึ่งด่วนหรือฉันรีบ หรือเมื่อจำนวนคำที่รวมกันถูก จำกัด ขอบเขต (เช่น <= 10) นั่นคือเหตุผลที่ฉันยังคงพูดถึงที่นี่ด้วยข้อแม้นี้ คุณยังสามารถใช้ tuples: ((f(x),g(x)) for ...), ()(หรือตามความคิดเห็นของ khachik โดยมีตัวสร้าง fg (x) ซึ่งให้ผลลัพธ์สองทูเพิล)


@ArashThr: มันกำลังทำ[f(1),g(1)] + [f(2),g(2)] + [f(3),g(3)] + ...
ninjagecko

คุณอธิบายได้ไหมว่ามันทำอะไรกันแน่?
Rsh

หมายเหตุ: สิ่งนี้มีรันไทม์ O (N ^ 2) ดังนั้นจึงอาจทำงานได้ช้าในรายการขนาดใหญ่
jamylak

1
@jamylak: ใช่ฉันพูดถึงสิ่งนี้ในคำตอบของคุณในความคิดเห็นด้วย =)
ninjagecko

ฉันถือว่าการละเมิดsum()ด้วยวิธีนี้เป็นการต่อต้านและฉันไม่เห็นเหตุผลใด ๆ ที่จะใช้มันไม่ว่าในกรณีใดก็ตาม รหัสในคำตอบอื่นของคุณพิมพ์น้อยลงดังนั้นแม้แต่ข้ออ้าง "เมื่อฉันต้องการซับสั้น ๆ หรือฉันกำลังรีบ" ก็ไม่ได้ตัดออกไป
Sven Marnach

2

ฟังก์ชันแลมบ์ดานี้ซิปสองรายการเป็นรายการเดียว:

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

ตัวอย่าง:

>>> 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 กำลังมองหาวิธีแก้ปัญหาความเข้าใจในรายการ แต่ฉันต้องการเสนอทางเลือกอื่นโดยใช้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)))

ซึ่งเร็วกว่าการใช้ความเข้าใจแบบ double list เล็กน้อย

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: 3.8.0.0


0

วิธีแก้ปัญหาโดยใช้การลด :

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)']

แม้ว่าจะไม่ใช่ความเข้าใจในรายการ แต่ก็เป็นวิธีที่ใช้งานได้ในการเข้าถึงปัญหา ความเข้าใจในรายการเป็นอีกวิธีหนึ่งในการรับmapข้อมูล แต่ในกรณีนี้การทำแผนที่ไม่ได้เป็นหนึ่งต่อหนึ่งระหว่างอินพุตและเอาต์พุตreduceจะช่วยให้มีห้องกระดิกพร้อมวิธีสร้างเอาต์พุตได้

โดยทั่วไปforการใช้งานรูปแบบใด ๆ:

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

(เช่นสำหรับลูปที่มีจุดประสงค์เพื่อสร้างผลข้างเคียงกับรายการหรือโครงสร้างข้อมูลที่คล้ายกัน)

สามารถปรับโครงสร้างใหม่เป็นการmap/reduce/filterใช้งานแบบเปิดเผยได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.