Toàn bộ danh sách: Trả lại hai (hoặc nhiều) mục cho mỗi mục


89

Có thể trả lại 2 (hoặc nhiều hơn) mục cho mỗi mục trong một danh sách hiểu không?

Những gì tôi muốn (ví dụ):

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

nên trở lại [f(0), g(0), f(1), g(1), ..., f(n-1), g(n-1)]

Vì vậy, một cái gì đó để thay thế khối mã này:

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

3
Vì tò mò, tại sao bạn muốn làm điều này? Có thể có một cách tốt hơn để đạt được mục tiêu cuối cùng của bạn mà không cần cố gắng làm theo cách này.
murgatroid99

3
Chủ yếu là vì tôi thích lập trình chức năng. Tôi muốn ánh xạ danh sách tọa độ thành một loạt tọa độ màn hình để sử dụng với hàm pyglet.graphics.draw.
Hashmush

Câu trả lời:


52
>>> 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]

Thời gian:

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
Mã này tạo ra các bộ giá trị không cần thiết (f(x), g(x)). Có thể được viết tốt hơn như: def fg(x): yield x + 2; yield x ** 2; list(chain.from_iterable(fg(x) for x in range(3))).
khachik

1
Bạn thậm chí có thể tổng quát hóa nó với chain.from_iterable((func(x) for func in funcs) for x in range(n))). Điều này sẽ vô tình loại bỏ khiếu nại của khachik. (Mặc dù trong một nghĩa nào đó, tôi và ông là về cơ bản giống trong điều kiện của quá trình Chúng tôi chỉ cần xác định các máy phát điện bên trong khác nhau..)
JAB

sum(..., [])Câu trả lời này tốt hơn câu trả lời của tôi vì nó không yêu cầu tạo lại danh sách trên mỗi dấu + (do đó có hiệu suất O (N) hơn là hiệu suất O (N ^ 2)). Tôi sẽ vẫn sử dụng sum(..., [])khi tôi muốn viết nhanh một chữ hoặc tôi đang vội, hoặc khi số lượng từ được kết hợp bị giới hạn (ví dụ: <= 10).
ninjagecko

@khachik Tôi nghĩ điều này sẽ nhanh hơn nhưng tôi sẽ tính thời gian cho cả hai phương pháp ngay bây giờ, các bộ giá trị được tạo rất nhanh trong python.
jamylak

3
Câu trả lời thứ ba, đã biến mất, trông như thế này: [y for x in range(n) for y in (f(x), g(x))]Nhưng điều này có lẽ chậm hơn. @jamylak Bạn cũng có thể kiểm tra điều này nếu bạn muốn.
Hashmush

118

Hiểu danh sách kép:

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

Bản giới thiệu:

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

10
Điều này rất hay vì nó cho thấy rằng các comp trong danh sách kép không quá đáng sợ: chúng chỉ đơn giản là lồng vào các vòng lặp for được viết giống như vòng lặp for . for x in range(5): for f in (f1, f2): newlist.append(f(x)). Tôi đã từng thấy chúng hơi khó hiểu vì tôi cứ cố gắng đảo ngược thứ tự.
DSM

1
Đây phải là câu trả lời được chấp nhận, cảm ơn bạn, tuyệt vời!
Wingjam

@DSM tôi nghĩ, nó sẽ khó hiểu mãi mãi.)
Winand

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

Điều này tương đương với [f(1),g(1)] + [f(2),g(2)] + [f(3),g(3)] + ...

Bạn cũng có thể nghĩ về nó như là:

def flatten(list):
    ...

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

lưu ý: Cách đúng là sử dụng itertools.chain.from_iterablehoặc hiểu danh sách kép. (Nó không yêu cầu tạo lại danh sách trên mỗi +, do đó có hiệu suất O (N) hơn là hiệu suất O (N ^ 2).) Tôi sẽ vẫn sử dụng sum(..., [])khi tôi muốn có một lớp lót nhanh hoặc tôi đang vội , hoặc khi số lượng các số hạng được kết hợp bị giới hạn (ví dụ: <= 10). Đó là lý do tại sao tôi vẫn đề cập đến nó ở đây, với lời cảnh báo này. Bạn cũng có thể sử dụng bộ giá trị: ((f(x),g(x)) for ...), ()(hoặc theo nhận xét của khachik, có một trình tạo fg (x) tạo ra một bộ hai bộ).


@ArashThr: nó đang làm[f(1),g(1)] + [f(2),g(2)] + [f(3),g(3)] + ...
ninjagecko

Bạn có thể giải thích chính xác nó đang làm gì không?
Rsh

Lưu ý: Điều này có thời gian chạy O (N ^ 2) nên nó có thể chậm trên các danh sách lớn.
jamylak

1
@jamylak: vâng, tôi cũng đã đề cập điều này trong câu trả lời của bạn trong phần bình luận. =)
ninjagecko

Tôi coi việc lạm dụng sum()theo cách này là một phản vật chất, và tôi không thấy có lý do gì để sử dụng nó trong bất kỳ trường hợp nào. Mã trong câu trả lời khác của bạn ít phải nhập hơn, vì vậy ngay cả lý do "khi tôi muốn viết nhanh một dòng hoặc tôi đang vội" cũng không thực sự cắt được nó.
Sven Marnach

2

Hàm lambda này nén hai danh sách thành một danh sách:

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

Thí dụ:

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

Tôi biết OP đang tìm kiếm một giải pháp hiểu danh sách, nhưng tôi muốn đưa ra một giải pháp thay thế bằng cách sử dụng 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)))

nhanh hơn một chút so với sử dụng khả năng hiểu danh sách kép.

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)

Phiên bản Python: 3.8.0


0

Một giải pháp sử dụng giảm :

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

Mặc dù không phải là một cách hiểu danh sách, nhưng đây là một cách tiếp cận vấn đề theo chức năng. Hiểu danh sách về cơ bản là một cách khác để nhập mapdữ liệu, nhưng trong trường hợp này, khi ánh xạ không phải là 1-1 giữa đầu vào và đầu ra, reducecho phép một số chỗ trống với cách tạo đầu ra.

Nói chung, bất kỳ fortriển khai nào của biểu mẫu:

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

(Tức là đối với các vòng lặp nhằm tạo ra hiệu ứng phụ trên danh sách hoặc cấu trúc dữ liệu tương tự)

Có thể được cấu trúc lại thành một map/reduce/filtertriển khai khai báo .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.