Đầu và đuôi trên một dòng


90

Có cách nào dễ hiểu để giải nén một danh sách trong phần tử đầu tiên và "đuôi" trong một lệnh duy nhất không?

Ví dụ:

>> head, tail = **some_magic applied to** [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
>> head
1
>>> tail
[1, 2, 3, 5, 8, 13, 21, 34, 55]

9
Hãy nhớ rằng danh sách không được triển khai dưới dạng danh sách được liên kết đơn lẻ trong Python, vì vậy thao tác này rất tốn kém (như trong: toàn bộ danh sách cần được sao chép). Tùy thuộc vào những gì bạn muốn đạt được, điều này có thể là một vấn đề. Tôi chỉ đề cập đến điều đó bởi vì loại cấu trúc danh sách này thường được tìm thấy trong các ngôn ngữ chức năng, nơi nó thực sự là một hoạt động rất rẻ.
Niklas B.

Câu trả lời:


189

Trong Python 3.x, bạn có thể thực hiện điều này một cách độc đáo:

>>> head, *tail = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
>>> head
1
>>> tail
[1, 2, 3, 5, 8, 13, 21, 34, 55]

Một tính năng mới trong 3.x là sử dụng *toán tử để giải nén, có nghĩa là bất kỳ giá trị bổ sung nào. Nó được mô tả trong PEP 3132 - Mở rộng có thể lặp lại mở rộng . Điều này cũng có lợi thế là làm việc trên bất kỳ chuỗi nào có thể lặp lại, không chỉ chuỗi.

Nó cũng thực sự dễ đọc.

Như được mô tả trong PEP, nếu bạn muốn làm điều tương đương dưới 2.x (mà không có khả năng tạo danh sách tạm thời), bạn phải thực hiện điều này:

it = iter(iterable)
head, tail = next(it), list(it)

Như đã lưu ý trong các nhận xét, điều này cũng tạo cơ hội để nhận giá trị mặc định headthay vì ném một ngoại lệ. Nếu bạn muốn hành vi này, hãy next()lấy đối số thứ hai tùy chọn với giá trị mặc định, vì vậy next(it, None)sẽ cung cấp cho bạn Nonenếu không có phần tử head.

Đương nhiên, nếu bạn đang làm việc trên một danh sách, cách dễ nhất mà không có cú pháp 3.x là:

head, tail = seq[0], seq[1:]

1
xin lỗi, tôi đã sử dụng thuật ngữ đuôi không thành công. Ý tôi là những gì tôi nói trong ví dụ, đó là danh sách không có phần tử đầu tiên
Giacomo d'Antonio

1
@NikolayFominyh Cả hai đều giống nhau - đều lấy phần tử đầu và tạo một danh sách mới chứa các phần tử đuôi. Không có sự khác biệt về độ phức tạp. Một lớp khác có thể triển khai __getitem__/ thực hiện __setitem__thao tác đuôi một cách lười biếng, nhưng danh sách tích hợp thì không.
Gareth Latty

2
Trong danh sách 800 phần tử thực hiện nó 1M lần, tôi có 2,8 giây cho giải pháp head, * tail = seq và chỉ 1,8 giây cho giải pháp head, tail = seq [0], seq [1:]. Việc cắt lát vẫn nhanh hơn cho các danh sách.
Cabu

2
@CMCDragonkai Không, lớp danh sách chính của Python là một danh sách mảng. Đây sẽ là O (n) vì nó liên quan đến việc sao chép phần đuôi vào một danh sách mới (với một chữ O (1) lấy phần đầu).
Gareth Latty

1
Cú pháp đẹp Đây là một lý do để chuyển sangpython 3.x
eigenfield

36
>>> mylist = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
>>> head, tail = mylist[0], mylist[1:]
>>> head
1
>>> tail
[1, 2, 3, 5, 8, 13, 21, 34, 55]

9

Tuy nhiên, đối với độ phức tạp của head,tailhoạt động O (1), bạn nên sử dụng deque.

Theo đường này:

from collections import deque
l = deque([1,2,3,4,5,6,7,8,9])
head, tail = l.popleft(), l

Nó hữu ích khi bạn phải lặp qua tất cả các phần tử của danh sách. Ví dụ trong hợp nhất ngây thơ 2 phân vùng trong sắp xếp hợp nhất.


Có vẻ như deque (list_instance) có độ phức tạp O (N). Liệu tôi có sai?
Никита Конин

1
@ НикитаКонин, bạn nói đúng về việc xây dựng deque. Tuy nhiên, nếu bạn muốn truy cập phần tử đầu tiên nhiều hơn một lần, thì head, tail = l.popleft(), l~ O (1). head, tail = seq[0], seq[1:]là O (n).
Nikolay Fominyh

Có vẻ như bạn chỉ có thể làm head = l.popleft()tailchỉ là một bí danh cho l. Nếu lthay tailđổi cũng thay đổi.
kon psych

2

Python 2, sử dụng lambda

>>> head, tail = (lambda lst: (lst[0], lst[1:]))([1, 1, 2, 3, 5, 8, 13, 21, 34, 55])
>>> head
1
>>> tail
[1, 2, 3, 5, 8, 13, 21, 34, 55]

1
tại sao trên thế giới bạn sẽ làm điều này thay vì chỉ head, tail = lst[0], lst[1:]? nếu phương tiện OP sử dụng một chữ sau đó ông có thể chia đầu và đuôi bằng tayhead, tail = 1, [1, 2, 3, 5, 8, 13, 21, 34, 55]
Filipe Pina

1
(1) Câu hỏi của Op là liệu có thể làm điều này trong một dòng (vì vậy không có lst = ...ở dòng trước). (2) Việc làm head, tail = lst[0], lst[1:]khiến mã mở ra các tác dụng phụ (xem xét head, tail = get_list()[0], get_list()[1:]), và khác với hình thức của Op head, tail = **some_magic applied to** [1, 1, 2, 3, 5, 8, 13, 21, 34, 55].
BobIsNotMyName

Nói như vậy, tôi thừa nhận rằng đây là một cách tồi tệ để có được phần đầu / phần đuôi. Nhưng tôi nghĩ đó là câu trả lời tốt nhất cho Python 2 cho câu hỏi cụ thể của Op.
BobIsNotMyName

1

Xây dựng dựa trên giải pháp Python 2 từ @GarethLatty , sau đây là một cách để lấy một dòng tương đương mà không có biến trung gian trong Python 2.

t=iter([1, 1, 2, 3, 5, 8, 13, 21, 34, 55]);h,t = [(h,list(t)) for h in t][0]

Nếu bạn cần nó để chống ngoại lệ (tức là hỗ trợ danh sách trống), thì hãy thêm:

t=iter([]);h,t = ([(h,list(t)) for h in t]+[(None,[])])[0]

Nếu bạn muốn làm điều đó mà không có dấu chấm phẩy, hãy sử dụng:

h,t = ([(h,list(t)) for t in [iter([1,2,3,4])] for h in t]+[(None,[])])[0]
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.