หัวและหางเป็นเส้นเดียว


93

มีวิธีการคลายแพ็กรายการในองค์ประกอบแรกและ "tail" ในคำสั่งเดียวหรือไม่?

ตัวอย่างเช่น:

>> 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
โปรดจำไว้ว่ารายการจะไม่ถูกนำไปใช้เป็นรายการที่เชื่อมโยงแบบเดี่ยวใน Python ดังนั้นการดำเนินการนี้จึงมีค่าใช้จ่ายสูง (เช่นเดียวกับ: รายการทั้งหมดจะต้องถูกคัดลอก) ขึ้นอยู่กับสิ่งที่คุณต้องการบรรลุสิ่งนี้อาจเป็นปัญหาหรือไม่ก็ได้ ฉันแค่พูดถึงว่าเนื่องจากการทำลายโครงสร้างรายการประเภทนี้มักพบในภาษาที่ใช้งานได้ซึ่งเป็นการดำเนินการที่ถูกมาก
Niklas B.

คำตอบ:


193

ภายใต้ Python 3.x คุณสามารถทำได้อย่างดี:

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

คุณลักษณะใหม่ใน 3.x คือการใช้ตัว*ดำเนินการในการแกะกล่องเพื่อหมายถึงค่าพิเศษใด ๆ มีอธิบายไว้ในPEP 3132 - Extended Iterable Unpackingเอาออก นอกจากนี้ยังมีข้อดีของการทำงานกับการทำซ้ำใด ๆ ไม่ใช่แค่ลำดับ

นอกจากนี้ยังสามารถอ่านได้จริงๆ

ตามที่อธิบายไว้ใน PEP หากคุณต้องการทำสิ่งที่เทียบเท่าภายใต้ 2.x (โดยไม่ต้องสร้างรายการชั่วคราว) คุณต้องทำสิ่งนี้:

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

ตามที่ระบุไว้ในความคิดเห็นสิ่งนี้ยังให้โอกาสในการรับค่าเริ่มต้นheadแทนที่จะโยนข้อยกเว้น หากคุณต้องการพฤติกรรมนี้ให้next()ใช้อาร์กิวเมนต์ที่สองซึ่งเป็นทางเลือกพร้อมค่าเริ่มต้นดังนั้นnext(it, None)จะให้คุณNoneถ้าไม่มีองค์ประกอบส่วนหัว

โดยปกติหากคุณกำลังทำงานในรายการวิธีที่ง่ายที่สุดที่ไม่มีไวยากรณ์ 3.x คือ:

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

1
ขออภัยฉันใช้คำว่า tail ไม่ถูกต้อง ฉันหมายถึงสิ่งที่ฉันพูดในตัวอย่างนั่นคือรายการที่ไม่มีองค์ประกอบแรก
Giacomo d'Antonio

1
@NikolayFominyh ทั้งคู่เหมือนกัน - ทั้งคู่ใช้องค์ประกอบส่วนหัวและสร้างรายการใหม่ที่มีองค์ประกอบหาง ไม่มีความซับซ้อนแตกต่างกัน คลาสอื่นสามารถใช้__getitem__/ __setitem__เพื่อดำเนินการหางได้อย่างเฉื่อยชา แต่รายการในตัวไม่ทำ
Gareth Latty

2
ในรายการ 800 องค์ประกอบที่ทำ 1 ล้านครั้งฉันมี 2.8s สำหรับ head, * tail = seq solution และ 1.8s สำหรับ head, tail = seq [0], seq [1:] solution เท่านั้น การแบ่งส่วนยังเร็วกว่าสำหรับรายการ
Cabu

2
@CMCDragonkai ไม่ใช่คลาสรายการหลักของ Python คือรายการอาร์เรย์ นี่จะเป็น O (n) เนื่องจากเกี่ยวข้องกับการคัดลอกหางไปยังรายการใหม่ (โดยมีหนึ่ง O (1) รับสำหรับส่วนหัว)
Gareth Latty

1
ไวยากรณ์ที่ดีนี้เป็นอีกเหตุผลหนึ่งที่จะย้ายไปที่python 3.x
eigenfield


9

สำหรับความซับซ้อนของhead,tailการดำเนินการO (1) คุณควรใช้dequeอย่างไร

วิธีต่อไปนี้:

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

จะมีประโยชน์เมื่อคุณต้องวนซ้ำองค์ประกอบทั้งหมดของรายการ ตัวอย่างเช่นในการผสาน 2 พาร์ติชันแบบไร้เดียงสาในการเรียงลำดับการผสาน


ดูเหมือนว่า deque (list_instance) มีความซับซ้อน O (N) ฉันผิดเหรอ?
НикитаКонин

1
@ НикитаКонинคุณพูดถูกเกี่ยวกับการก่อสร้าง deque อย่างไรก็ตามหากคุณต้องการเข้าถึงองค์ประกอบแรกมากกว่าหนึ่งครั้งก็head, tail = l.popleft(), lคือ ~ O (1) head, tail = seq[0], seq[1:]คือ O (n)
Nikolay Fominyh

ดูเหมือนว่าคุณก็สามารถทำhead = l.popleft()และเป็นเพียงนามแฝงสำหรับtail lหากมีlการtailเปลี่ยนแปลงเปลี่ยนแปลงด้วย.
kon psych

2

Python 2 โดยใช้ 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
ทำไมในโลกนี้คุณถึงทำสิ่งนี้แทนที่จะเป็นเพียงแค่head, tail = lst[0], lst[1:]? ถ้า OP หมายถึงการใช้ตัวอักษรเขาก็สามารถแยกหัวและหางได้ด้วยตนเองhead, tail = 1, [1, 2, 3, 5, 8, 13, 21, 34, 55]
Filipe Pina

1
(1) คำถามของ Op คือเป็นไปได้หรือไม่ที่จะทำสิ่งนี้ในบรรทัดเดียว (ไม่ใช่lst = ...ในบรรทัดก่อนหน้า) (2) การทำhead, tail = lst[0], lst[1:]ใบเปิดรหัสผลข้างเคียง (พิจารณาhead, tail = get_list()[0], get_list()[1:]) head, tail = **some_magic applied to** [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]และแตกต่างจากรูปแบบของสหกรณ์
BobIsNotMyName

ดังที่กล่าวมาฉันยอมรับว่านี่เป็นวิธีที่ไม่ดีในการรับหัว / หาง แต่ฉันคิดว่ามันเป็นคำตอบที่ดีที่สุดสำหรับ Python 2 สำหรับคำถามเฉพาะของ Op
BobIsNotMyName

1

การสร้างโซลูชัน Python 2 จาก @GarethLattyต่อไปนี้เป็นวิธีรับบรรทัดเดียวที่เทียบเท่าโดยไม่มีตัวแปรกลางใน Python 2

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

หากคุณต้องการให้เป็นข้อยกเว้น (เช่นรองรับรายการว่าง) ให้เพิ่ม:

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

หากคุณต้องการทำโดยไม่มีอัฒภาคให้ใช้:

h,t = ([(h,list(t)) for t in [iter([1,2,3,4])] for h in t]+[(None,[])])[0]
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.