ทำไมหลามจึงไม่มีฟังก์ชั่น“ เรียบ” สำหรับรายการ?


39

Erlang และ Ruby ทั้งสองมาพร้อมกับฟังก์ชั่นสำหรับอาร์เรย์ที่แบนราบ ดูเหมือนว่าเป็นเครื่องมือที่ง่ายและมีประโยชน์ในการเพิ่มภาษา เราสามารถทำได้:

>>> mess = [[1, [2]], 3, [[[4, 5]], 6]]
>>> mess.flatten()
[1, 2, 3, 4, 5, 6]

หรือแม้กระทั่ง:

>>> import itertools
>>> mess = [[1, [2]], 3, [[[4, 5]], 6]]
>>> list(itertools.flatten(mess))
[1, 2, 3, 4, 5, 6]

ใน Python ผู้ใช้จะต้องพบกับปัญหาในการเขียนฟังก์ชั่นเพื่อให้อาร์เรย์เรียบตั้งแต่เริ่มต้น เรื่องนี้ดูจะโง่สำหรับฉัน มันเหมือนกับการต้องเขียนฟังก์ชั่นที่กำหนดเองเพื่อเชื่อมสองอาร์เรย์

ฉันได้ Googled ไร้ผลดังนั้นฉันจึงถามที่นี่; มีเหตุผลบางอย่างไหมว่าทำไมภาษาผู้ใหญ่เช่น Python 3 ซึ่งมาพร้อมกับแบตเตอรี่หลายแสนตัวรวมอยู่ด้วยไม่ได้มีวิธีการที่เรียบง่ายในการจัดเรียงอาเรย์? แนวคิดในการรวมฟังก์ชั่นดังกล่าวได้รับการพูดคุยและปฏิเสธในบางประเด็นหรือไม่?


2
@detly: ฉันพลาดแบนเมื่อใช้หลายแบบสอบถามเพื่อดึงข้อมูลจากแหล่งที่แตกต่างกัน แบบสอบถามแต่ละรายการส่งคืนรายการพจนานุกรมดังนั้นในที่สุดฉันก็มีรายการของพจนานุกรมที่จะเปลี่ยนเป็นรายการพจนานุกรม ฉันใช้ลูป + extendแต่แบนจะสวยกว่านี้มาก อย่างไรก็ตามฉันกระทบกระเทือนหากรูปแบบนี้เป็นเรื่องปกติมากพอที่จะแสดงให้เห็นว่าแบนในห้องสมุดมาตรฐาน
จอร์โจ

4
"ฉันหมายความว่าลองคิดดูว่าคุณมีจุดบกพร่องในรหัสของคุณหรือไม่ซึ่งการเปลี่ยนแปลงโครงสร้างข้อมูลของคุณโดยไม่ตั้งใจแบนยังคงใช้ได้ แต่ให้ผลลัพธ์ที่ผิดโดยสมบูรณ์": นี่คือเหตุผลหนึ่งว่าทำไมฉันชอบภาษาที่พิมพ์แบบคงที่ ;-)
Giorgio


2
@BryanOakley ดูความคิดเห็นก่อนหน้านี้ด้วย (แม้ว่าจะไม่ใช่รายการที่มีหลายระดับ แต่โดยทั่วไปก็เป็นเรื่องธรรมดา)
Izkata

3
มันติดตั้งใน Mathemaica และฉันใช้มันอย่างกว้างขวาง
ต่อ Alexandersson

คำตอบ:


34

ข้อเสนอสำหรับflattenฟังก์ชั่นที่จะเพิ่มไปยังไลบรารีมาตรฐานจะปรากฏเป็นครั้งคราวในรายการส่งเมลของpython-devและpython-ideas นักพัฒนางูใหญ่มักตอบสนองด้วยประเด็นต่อไปนี้:

  1. การปรับระดับหนึ่งระดับ (การเปลี่ยนการวนซ้ำเป็นการวนซ้ำเดียว) เป็นการแสดงออกบรรทัดเดียวที่ไม่สำคัญ(x for y in z for x in y)และในกรณีใดก็ตามอยู่ในไลบรารีมาตรฐานภายใต้ชื่อitertools.chain.from_iterableแล้ว

  2. กรณีการใช้งานสำหรับแบนหลายระดับเอนกประสงค์มีอะไรบ้าง? สิ่งเหล่านี้น่าสนใจมากพอที่จะเพิ่มฟังก์ชั่นในไลบรารีมาตรฐานหรือไม่?

  3. วิธีการใช้งานหลายระดับโดยทั่วไปจะมีวิธีการกำหนดให้แบนเมื่อใดและจะต้องออกไปคนเดียวเมื่อใด คุณอาจคิดว่ากฎเช่น "อะไรที่เรียบที่รองรับการเชื่อมต่อ iterable" จะทำงาน แต่ที่จะนำไปสู่วง จำกัด flatten('a')สำหรับ

ดูตัวอย่างRaymond Hettinger :

มันได้รับการกล่าวถึงโฆษณา nauseamบน comp.lang.python ผู้คนดูเหมือนจะสนุกไปกับการเขียนเวอร์ชันแบน ๆ ของตนเองมากกว่าการค้นหากรณีการใช้ที่ถูกกฎหมายที่ยังไม่มีวิธีแก้ปัญหาเล็กน้อย

เครื่องปรับเอนกประสงค์ใช้ต้องการวิธีที่จะบอกได้ว่าอะตอมคืออะไรและสามารถแบ่งย่อยได้มากขึ้น นอกจากนี้ยังไม่ชัดเจนว่าจะขยายอัลกอริทึมให้ครอบคลุมอินพุตด้วยโครงสร้างข้อมูลแบบต้นไม้ที่มีข้อมูลที่โหนดและใบไม้อย่างไร (preorder, postorder, inorder traversal เป็นต้น)


เพียงเพื่อจะชัดเจนที่นี้หมายถึงว่าในระดับหนึ่งฟังก์ชั่นสามารถกำหนดเป็นflatten lambda z: [x for y in z for x in y]
Christopher Martin

1
"เครื่องมือที่ใช้งานทั่วไปต้องการวิธีที่จะบอกได้ว่าอะไรคืออะตอมและอะไรที่สามารถแบ่งย่อยได้อีก": นี่ฟังดูเหมือนปัญหาที่สามารถแก้ไขได้โดยใช้ OOP: แต่ละวัตถุอาจมีflattenวิธี การดำเนินการตามวิธีนี้ควรเรียกซ้ำflattenคอมโพเนนต์ย่อยหากวัตถุเป็นคอมโพสิต น่าเสียดายที่ AFAIK ไม่ใช่ทุกค่าที่เป็นวัตถุใน Python ใน Ruby มันควรทำงานแม้ว่า
Giorgio

1
ผู้ช่วยที่แบนสำหรับระดับเดียวแทนที่จะเป็นแบบต่อเนื่อง "สำหรับใน" เป็นกรณีที่ดีพอ IMO สามารถอ่านได้ง่าย
dtc

2
@Giorgio Python ไม่ชอบวิธีการดังกล่าว โปรโตคอลเป็นที่ต้องการและฉันพบว่าพวกเขาทำงานได้ราบรื่นกว่าการออกแบบ OOP เนื่องจากคุณไม่จำเป็นต้องใช้งานมากนัก
jpmc26

8

มันมาพร้อมกับวิธีการดังกล่าว แต่ก็ไม่ได้เรียกว่าแบน มันเรียกว่า " โซ่ " มันจะส่งคืนตัววนซ้ำซึ่งคุณจะต้องใช้ฟังก์ชั่น list () เพื่อเปลี่ยนกลับเป็นรายการ หากคุณไม่ต้องการใช้ * คุณสามารถใช้รุ่น "from_iterator" รุ่นที่สอง มันทำงานเหมือนกันใน Python 3 มันจะล้มเหลวถ้า input list ไม่ใช่ list ของ list

[[1], [2, 3], [3, 4, 5]] #yes
[1, 2, [5, 6]] #no

มีอยู่ครั้งหนึ่งที่วิธีการแบนในโมดูล compiler.ast แต่สิ่งนี้ถูกคัดค้านใน 2.6 แล้วลบออกใน 3.0 การเรียกซ้ำความลึกตามอำเภอใจจำเป็นสำหรับรายการที่ซ้อนกันตามอำเภอใจไม่สามารถทำงานได้ดีกับความลึกในการเรียกซ้ำสูงสุดที่เข้มงวดของ Python เหตุผลสำหรับการกำจัดของคอมไพเลอร์เป็นส่วนใหญ่เพราะมันเป็นระเบียบ คอมไพเลอร์ก็กลายเป็นastแต่แบนถูกทิ้งไว้ข้างหลัง

ความลึกตามอำเภอใจสามารถทำได้ด้วยอาร์เรย์ของ numpy และความแบนของห้องสมุด


chain.from_iteratorฟังก์ชั่นที่คุณกล่าวว่าเท่านั้นที่สามารถนำมาใช้เพื่อแผ่รายการสองมิติ จริงฟังก์ชั่นแผ่ซึ่งยอมรับใด ๆจำนวนของรายการที่ซ้อนกันและผลตอบแทนที่รายการหนึ่งมิติยังจะเป็นประโยชน์อย่างหนาแน่นในหลายกรณี (อย่างน้อยในความคิดของฉัน)
Hubro

2
@Hubro: "ในหลาย ๆ กรณี" - คุณสามารถตั้งชื่อหกคนได้หรือไม่?
Gareth Rees

1
@GarethRees: ฉันให้ตัวอย่างที่นี่: programmers.stackexchange.com/questions/254279/ …
Hubro

ฉันจะไปไกลเกินกว่าที่จะโต้แย้งว่าหากภาษาอื่นเหล่านี้มีคุณสมบัติดังกล่าวในการทำให้รายการเรียบง่ายขึ้นตามที่อธิบายไว้นั่นเป็นหนึ่งในข้อโต้แย้งที่น่าสนใจที่สุดที่สนับสนุนการเพิ่มความสามารถอย่างง่ายของ Python
Bobort

มันส่งคืนตัววนซ้ำหรือตัวสร้างหรือไม่
jpmc26

-1

... อาจเป็นเพราะการเขียนด้วยตนเองไม่ใช่เรื่องยาก

def flatten(l): return flatten(l[0]) + (flatten(l[1:]) if len(l) > 1 else []) if type(l) is list else [l]

... แล้วแผ่สิ่งที่คุณต้องการ :)

>>> flatten([1,[2,3],4])
[1, 2, 3, 4]
>>> flatten([1, [2, 3], 4, [5, [6, {'name': 'some_name', 'age':30}, 7]], [8, 9, [10, [11, [12, [13, {'some', 'set'}, 14, [15, 'some_string'], 16], 17, 18], 19], 20], 21, 22, [23, 24], 25], 26, 27, 28, 29, 30])
[1, 2, 3, 4, 5, 6, {'age': 30, 'name': 'some_name'}, 7, 8, 9, 10, 11, 12, 13, set(['set', 'some']), 14, 15, 'some_string', 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
>>> 

8
ผู้ถามทราบว่า: "ใน Python เราต้องผ่านปัญหาในการเขียนฟังก์ชั่นเพื่อให้อาร์เรย์เรียบตั้งแต่เริ่มต้น" นี่ไม่ใช่ความพยายามที่จะตอบคำถามที่ถาม "ดูเหมือนว่าฉันจะงี่เง่าอาเรย์แบบแบน ๆ เป็นเรื่องธรรมดาที่ต้องทำมันก็เหมือนกับการต้องเขียนฟังก์ชั่นที่กำหนดเองสำหรับการต่อสองอาร์เรย์"
ริ้น

1
จากหัวข้อ ... แต่สุดยอด :-) !!
SeF

คำตอบนี้เหมือนกับบอก OP ว่าเขาไม่ใช่นักพัฒนาที่ดีเพราะเขาไม่รู้วิธีเขียนโค้ดฟังก์ชันเอง ฉันขอแนะนำให้คุณแก้ไขจุดเริ่มต้นของคำตอบของคุณเพราะนี่เป็นรหัสที่มีประโยชน์สำหรับผู้ที่สะดุดกับคำถามแม้ว่าจะอยู่นอกหัวข้อ
Federico Bonelli
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.