เรียบรายการที่ผิดปกติ


440

ใช่ฉันรู้ว่าเรื่องนี้ได้รับการคุ้มครองก่อน ( นี่ , ที่นี่ , ที่นี่ , ที่นี่ ) แต่เท่าที่ผมรู้ว่าการแก้ปัญหาทั้งหมดยกเว้นหนึ่งล้มเหลวในรายการเช่นนี้:

L = [[[1, 2, 3], [4, 5]], 6]

ตำแหน่งที่ต้องการออก

[1, 2, 3, 4, 5, 6]

หรืออาจจะดียิ่งขึ้นเป็นตัววนซ้ำ ทางออกเดียวที่ฉันเห็นว่าใช้ได้สำหรับการทำรังโดยพลการพบในคำถามนี้ :

def flatten(x):
    result = []
    for el in x:
        if hasattr(el, "__iter__") and not isinstance(el, basestring):
            result.extend(flatten(el))
        else:
            result.append(el)
    return result

flatten(L)

นี่เป็นรุ่นที่ดีที่สุดหรือไม่? ฉันมองข้ามบางสิ่งไปหรือเปล่า มีปัญหาอะไรบ้าง?


16
ความจริงที่ว่ามีคำตอบมากมายและการตอบคำถามนี้แสดงให้เห็นว่านี่ควรจะเป็นฟังก์ชั่นในตัวใช่ไหม? มันเลวร้ายเกินไปโดยเฉพาะอย่างยิ่ง compiler.ast ถูกลบออกจาก Python 3.0
Mittenchops

3
ฉันจะบอกว่าสิ่งที่หลามต้องการจริงๆคือการเรียกซ้ำแบบไม่แตกสลายแทนที่จะเป็นแบบบิลท์อินอื่น
ดิน

2
@Mittenchops: ทั้งหมดไม่เห็นด้วยความจริงที่ว่าคนที่ทำงานด้วย API ที่ไม่ดีอย่างเห็นได้ชัด / ซับซ้อนมากเกินไปโครงสร้างข้อมูล (เพียงบันทึก: lists ตั้งใจจะให้เป็นเนื้อเดียวกัน) ไม่ได้หมายความว่ามันเป็นความผิดของงูหลามและเราจำเป็นต้อง builtin สำหรับงานดังกล่าว
Azat Ibrakov

1
หากคุณสามารถเพิ่มแพ็คเกจให้กับโครงการของคุณได้ - ฉันคิดว่าโซลูชันmore_itertools.collapseจะทำดีที่สุด จากคำตอบนี้: stackoverflow.com/a/40938883/3844376
viddik13

คำตอบ:


382

การใช้ฟังก์ชันตัวสร้างสามารถทำให้ตัวอย่างของคุณง่ายขึ้นในการอ่านและอาจเพิ่มประสิทธิภาพ

Python 2

def flatten(l):
    for el in l:
        if isinstance(el, collections.Iterable) and not isinstance(el, basestring):
            for sub in flatten(el):
                yield sub
        else:
            yield el

ฉันใช้Iterable ABCเพิ่มใน 2.6

Python 3

ใน Python 3 basestringไม่มีอีกต่อไป แต่คุณสามารถใช้ tuple strและbytesเพื่อให้ได้ผลเช่นเดียวกัน

yield fromผู้ประกอบการส่งคืนสินค้าจากกำเนิดหนึ่งในช่วงเวลาที่ เพิ่มไวยากรณ์นี้สำหรับการมอบสิทธิ์ให้กับผู้สร้างย่อยใน 3.3

def flatten(l):
    for el in l:
        if isinstance(el, collections.Iterable) and not isinstance(el, (str, bytes)):
            yield from flatten(el)
        else:
            yield el

6
ข้อเสนอแนะทั้งหมดในหน้านี้เป็นเพียงคนเดียวที่แบนรายการนี้ได้ในพริบตาเมื่อฉันทำอย่างนี้l = ([[chr(i),chr(i-32)] for i in xrange(ord('a'), ord('z')+1)] + range(0,9)) list(flatten(l))คนอื่น ๆ ทั้งหมดจะเริ่มทำงานและใช้ตลอดไป!
nemesisfixx

7
นี่ยังทำให้พจนานุกรมเรียบ บางทีคุณอาจต้องการที่จะใช้collections.Sequenceแทนcollections.Iteratable?
josch

1
for i in flatten(42): print (i)นี้ไม่ได้ทำงานกับสิ่งที่ไม่ได้เป็นรายการแรกเช่น สิ่งนี้สามารถแก้ไขได้โดยการย้ายisinstance-test และ else-clause ด้านนอกของfor el-loop (จากนั้นคุณสามารถโยนอะไรลงไปแล้วมันจะทำให้รายชื่อออกมาจากมัน)
RolKau

6
สำหรับ Python 3.7 การcollections.Iterableเลิกใช้ ใช้collections.abc.Iterableแทน
dawg

5
แน่นอนว่าไม่จำเป็นต้องมีการสอบถามซ้ำ ในกรณีนี้การใช้การสอบถามซ้ำไม่ใช่วิธีที่ดีที่สุดเนื่องจากจะเกิดปัญหาในรายการที่ซ้อนกันลึก (ความลึก> 1,000) แต่ถ้าคุณไม่ได้ตั้งใจจะมีอะไรที่ปลอดภัยแล้วใช่ฟังก์ชั่นวนซ้ำจะดีกว่าเพราะอ่าน / เขียนได้ง่ายขึ้น
cglacet

50

ทางออกของฉัน:

import collections


def flatten(x):
    if isinstance(x, collections.Iterable):
        return [a for i in x for a in flatten(i)]
    else:
        return [x]

กระชับกว่าเล็กน้อย แต่ก็เหมือนกันมาก


5
คุณสามารถทำสิ่งนี้ได้โดยไม่ต้องนำเข้าอะไรเลยถ้าคุณเพียงแค่try: iter(x)ทดสอบว่า iterable หรือไม่… แต่ฉันไม่คิดว่าจะต้องนำเข้าโมดูล stdlib นั้นเป็นข้อเสียที่ควรหลีกเลี่ยง
abarnert

8
ควรทราบว่าวิธีนี้ใช้ได้เฉพาะเมื่อรายการทั้งหมดเป็นประเภทint
alfasin

1
อาจทำให้รัดกุมขึ้นdef flatten(x): return [a for i in x for a in flatten(i)] if isinstance(x, collections.Iterable) else [x]- แต่ความสามารถในการอ่านอาจเป็นอัตนัยที่นี่
ศูนย์

4
สิ่งนี้ใช้ไม่ได้กับสตริงเนื่องจากสตริงสามารถทำซ้ำได้เช่นกัน แทนที่เงื่อนไขด้วยif isinstance(x, collections.Iterable) and not isinstance(x, basestring)
aandis

36

เครื่องมือสร้างโดยใช้การเรียกซ้ำและพิมพ์เป็ด (อัปเดตสำหรับ Python 3):

def flatten(L):
    for item in L:
        try:
            yield from flatten(item)
        except TypeError:
            yield item

list(flatten([[[1, 2, 3], [4, 5]], 6]))
>>>[1, 2, 3, 4, 5, 6]

1
ขอบคุณที่ใช้งานได้ดีกับ Python 3 สำหรับ 2.x จำเป็นต้องมีก่อนหน้านี้: for i in flatten(item): yield i
dansalmo

รายการ (แบน ([['X'], 'Y'])) ล้มเหลวในตัวแปร
2.X

@ user1019129 ดูความคิดเห็นของฉันด้านบนของคุณ
dansalmo

ใช่มันล้มเหลวด้วยวงจร .. ผมคิดว่าเพราะสตริงยังเป็น "อาร์เรย์" -of-ตัวอักษร
สเตน

35

รุ่นตัวสร้างของโซลูชันที่ไม่เรียกซ้ำของ @ unutbu ตามที่ร้องขอโดย @Andrew ในความคิดเห็น:

def genflat(l, ltypes=collections.Sequence):
    l = list(l)
    i = 0
    while i < len(l):
        while isinstance(l[i], ltypes):
            if not l[i]:
                l.pop(i)
                i -= 1
                break
            else:
                l[i:i + 1] = l[i]
        yield l[i]
        i += 1

รุ่นที่เรียบง่ายขึ้นเล็กน้อยของเครื่องกำเนิดไฟฟ้านี้:

def genflat(l, ltypes=collections.Sequence):
    l = list(l)
    while l:
        while l and isinstance(l[0], ltypes):
            l[0:1] = l[0]
        if l: yield l.pop(0)

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

6
ฉันคิดว่าคุณต้องทดสอบสตริง - เช่นเพิ่ม "และไม่ใช่ isinstance (l [0], basestring)" เช่นเดียวกับวิธีแก้ปัญหาของ Cristian มิฉะนั้นคุณจะได้วนรอบไม่สิ้นสุดรอบ l [0: 1] = l [0]
c-urchin

นี่เป็นตัวอย่างที่ดีในการสร้างเครื่องกำเนิดไฟฟ้า แต่เมื่อ c-urchin กล่าวถึงอัลกอริทึมจะล้มเหลวเมื่อลำดับประกอบด้วยสตริง
แดเนียล 'แดง' Griffith

28

นี่คือเวอร์ชันการทำงานของฉันสำหรับการเรียกซ้ำแบบแบนซึ่งจัดการทั้ง tuples และรายการและช่วยให้คุณสามารถผสมอาร์กิวเมนต์ตำแหน่งใดก็ได้ ส่งคืนตัวกำเนิดซึ่งสร้างลำดับทั้งหมดตามลำดับโดย arg:

flatten = lambda *n: (e for a in n
    for e in (flatten(*a) if isinstance(a, (tuple, list)) else (a,)))

การใช้งาน:

l1 = ['a', ['b', ('c', 'd')]]
l2 = [0, 1, (2, 3), [[4, 5, (6, 7, (8,), [9]), 10]], (11,)]
print list(flatten(l1, -2, -1, l2))
['a', 'b', 'c', 'd', -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

1
ทางออกที่ดี แต่จะมีประโยชน์มากถ้าคุณเพิ่มความคิดเห็นบางอย่างที่จะอธิบายสิ่งที่e, a, nอ้างถึง
Kristof Pal

2
@WolfgangKuehne: ลองargsสำหรับn, intermediate(หรือสั้นmidหรือคุณอาจต้องการelement) สำหรับaและresultสำหรับeดังนั้น:flatten = lambda *args: (result for mid in args for result in (flatten(*mid) if isinstance(mid, (tuple, list)) else (mid,)))
หยุดชั่วคราวจนกว่าจะมีประกาศ

ซึ่งเร็วกว่าcompiler.ast.flattenมาก รหัสที่ยอดเยี่ยมขนาดกะทัดรัดใช้ได้กับประเภทวัตถุใด ๆ (ฉันคิดว่า)
bcdan

ว้าวนี่ควรเป็นคำตอบที่ได้รับการโหวตมากที่สุดและได้รับการยอมรับ ... ทำงานเหมือนมีเสน่ห์!
ส่งต่อ

27

เวอร์ชันนี้flattenหลีกเลี่ยงขีด จำกัด การเรียกซ้ำของไพ ธ อน มันเป็นเครื่องกำเนิดไฟฟ้าที่สามารถจัดการกับสตริงและ iterables โดยพลการ (แม้แต่ที่ไม่มีที่สิ้นสุด)

import itertools as IT
import collections

def flatten(iterable, ltypes=collections.Iterable):
    remainder = iter(iterable)
    while True:
        first = next(remainder)
        if isinstance(first, ltypes) and not isinstance(first, (str, bytes)):
            remainder = IT.chain(first, remainder)
        else:
            yield first

นี่คือตัวอย่างที่แสดงให้เห็นถึงการใช้งาน:

print(list(IT.islice(flatten(IT.repeat(1)),10)))
# [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

print(list(IT.islice(flatten(IT.chain(IT.repeat(2,3),
                                       {10,20,30},
                                       'foo bar'.split(),
                                       IT.repeat(1),)),10)))
# [2, 2, 2, 10, 20, 30, 'foo', 'bar', 1, 1]

print(list(flatten([[1,2,[3,4]]])))
# [1, 2, 3, 4]

seq = ([[chr(i),chr(i-32)] for i in range(ord('a'), ord('z')+1)] + list(range(0,9)))
print(list(flatten(seq)))
# ['a', 'A', 'b', 'B', 'c', 'C', 'd', 'D', 'e', 'E', 'f', 'F', 'g', 'G', 'h', 'H',
# 'i', 'I', 'j', 'J', 'k', 'K', 'l', 'L', 'm', 'M', 'n', 'N', 'o', 'O', 'p', 'P',
# 'q', 'Q', 'r', 'R', 's', 'S', 't', 'T', 'u', 'U', 'v', 'V', 'w', 'W', 'x', 'X',
# 'y', 'Y', 'z', 'Z', 0, 1, 2, 3, 4, 5, 6, 7, 8]

แม้ว่าflattenสามารถจัดการกับเครื่องกำเนิดไฟฟ้าที่ไม่มีที่สิ้นสุด แต่ก็ไม่สามารถจัดการกับการทำรังแบบไม่มีที่สิ้นสุด:

def infinitely_nested():
    while True:
        yield IT.chain(infinitely_nested(), IT.repeat(1))

print(list(IT.islice(flatten(infinitely_nested()), 10)))
# hangs

1
ฉันทามติใด ๆ เกี่ยวกับการใช้ ABC Iterable หรือ ABC Sequence?
Wim

sets, dicts, deques, listiterators, generators, filehandles และชั้นเรียนที่กำหนดเองกับ__iter__ที่กำหนดไว้ทุกกรณีแต่ไม่ได้collections.Iterable collections.Sequenceผลของการให้แบนราบdictเป็นบิตที่เต็มไปด้วยปัญหา แต่อย่างอื่นผมคิดว่าเป็นค่าเริ่มต้นได้ดีกว่าcollections.Iterable collections.Sequenceมันเป็นเสรีนิยมมากขึ้นอย่างแน่นอน
unutbu

@wim: ปัญหาอย่างหนึ่งของการใช้collections.Iterableคือมีตัวกำเนิดที่ไม่มีที่สิ้นสุด ฉันเปลี่ยนคำตอบของฉันแล้วจัดการกับกรณีนี้
unutbu

1
ดูเหมือนจะใช้งานไม่ได้กับตัวอย่างที่ 3 และ 4 StopIterationมันจะพ่น นอกจากนี้ดูเหมือนว่าจะถูกแทนที่ด้วยwhile True: first = next(remainder) for first in remainder:
Georgy

@Georgy try-except StopIteration blockนี้อาจจะแก้ไขด้วยการห่อหุ้มร่างกายของแผ่ใน
baduker

12

นี่เป็นคำตอบที่น่าสนใจยิ่งกว่า ...

import re

def Flatten(TheList):
    a = str(TheList)
    b,crap = re.subn(r'[\[,\]]', ' ', a)
    c = b.split()
    d = [int(x) for x in c]

    return(d)

โดยทั่วไปมันจะแปลงรายการที่ซ้อนกันเป็นสตริงใช้ regex เพื่อตัดไวยากรณ์ที่ซ้อนกันออกแล้วแปลงผลลัพธ์กลับไปเป็นรายการ (แบน)


หากคุณพยายามที่จะพูดถึงเรื่องนี้กับสิ่งอื่นที่ไม่ใช่ค่า int มันจะสนุกกับมันเช่น[['C=64', 'APPLE ]['], ['Amiga', 'Mac', 'ST']]:) ในอีกทางหนึ่งหากมีรายการที่มีตัวมันเองมันจะทำได้ดีกว่าคำตอบอื่น ๆ เล็กน้อย ยกเว้นแทนเพียงบ่วงจนกว่าคุณจะวิ่งออกมาจากหน่วยความจำ / recursing จนกว่าคุณจะหมดสแต็ค ...
abarnert

พรอมต์ดั้งเดิมเป็นเรื่องเกี่ยวกับรายการจำนวนเต็ม หากคุณเพิ่งเปลี่ยน list comprehension เป็น d = [x สำหรับ x ใน c] มันน่าจะใช้ได้ผลสำหรับตัวอย่างของคุณ
ดินเหนียว

ครั้งแรก[x for x in c]เป็นเพียงวิธีช้าและ verbose ในการทำสำเนาcดังนั้นทำไมคุณจะทำเช่นนั้น? ประการที่สองรหัสของคุณอย่างชัดเจนจะแปลง'APPLE ]['เข้าไป'APPLE 'เพราะมันไม่ได้จัดการกับข้อความมันก็ถือว่าวงเล็บใด ๆ วงเล็บรายการ
abarnert

ฮา! วิธีที่ความคิดเห็นของคุณได้รับการจัดรูปแบบบนคอมพิวเตอร์ของฉันฉันไม่ได้ตระหนักว่าควรจะเป็น Apple II ตามที่ปรากฏในคอมพิวเตอร์เครื่องเก่า ไม่ว่าในกรณีใดคำตอบของคำถามทั้งสองข้อของฉันคือแบบฝึกหัดนี้สำหรับฉันเป็นเพียงการทดลองเพื่อค้นหาวิธีแก้ปัญหาที่สร้างสรรค์เพื่อทำให้รายการเรียบ ฉันไม่แน่ใจว่าฉันจะพูดคุยเพื่อทำให้ทุกรายการเรียบ
ดินเหนียว

คุณเพียงแค่ต้องarr_str = str(arr)และจากนั้น[int(s) for s in re.findall(r'\d+', arr_str)]จริงๆ ดูgithub.com/jorgeorpinel/flatten_nested_lists/blob/master/…
Jorge Orpinel


8

คุณสามารถใช้deepflattenจากแพ็คเกจบุคคลที่สามiteration_utilities:

>>> from iteration_utilities import deepflatten
>>> L = [[[1, 2, 3], [4, 5]], 6]
>>> list(deepflatten(L))
[1, 2, 3, 4, 5, 6]

>>> list(deepflatten(L, types=list))  # only flatten "inner" lists
[1, 2, 3, 4, 5, 6]

มันเป็นตัววนซ้ำดังนั้นคุณจำเป็นต้องวนซ้ำ (ตัวอย่างเช่นล้อมด้วยlistหรือใช้เป็นวนซ้ำ) ภายในจะใช้วิธีการวนซ้ำแทนวิธีแบบเรียกซ้ำและเขียนเป็นส่วนขยาย C เพื่อให้สามารถเร็วกว่าวิธีแบบหลามบริสุทธิ์:

>>> %timeit list(deepflatten(L))
12.6 µs ± 298 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
>>> %timeit list(deepflatten(L, types=list))
8.7 µs ± 139 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

>>> %timeit list(flatten(L))   # Cristian - Python 3.x approach from https://stackoverflow.com/a/2158532/5393381
86.4 µs ± 4.42 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

>>> %timeit list(flatten(L))   # Josh Lee - https://stackoverflow.com/a/2158522/5393381
107 µs ± 2.99 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

>>> %timeit list(genflat(L, list))  # Alex Martelli - https://stackoverflow.com/a/2159079/5393381
23.1 µs ± 710 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

ฉันเป็นผู้เขียนiteration_utilitiesห้องสมุด


7

มันสนุกที่ได้พยายามสร้างฟังก์ชั่นที่สามารถทำให้รายการผิดปกติใน Python เรียบ แต่แน่นอนว่าเป็นสิ่งที่ Python ใช้ (เพื่อทำให้การเขียนโปรแกรมสนุก) เครื่องกำเนิดไฟฟ้าต่อไปนี้ทำงานได้ค่อนข้างดีกับข้อแม้บางประการ:

def flatten(iterable):
    try:
        for item in iterable:
            yield from flatten(item)
    except TypeError:
        yield iterable

มันจะแผ่ประเภทข้อมูลที่คุณอาจต้องการเหลือเพียงอย่างเดียว (เช่นbytearray, bytesและstrวัตถุ) นอกจากนี้รหัสอาศัยอยู่กับความจริงที่ว่าขอ iterator จาก non-iterable TypeErrorยก

>>> L = [[[1, 2, 3], [4, 5]], 6]
>>> def flatten(iterable):
    try:
        for item in iterable:
            yield from flatten(item)
    except TypeError:
        yield iterable


>>> list(flatten(L))
[1, 2, 3, 4, 5, 6]
>>>

แก้ไข:

ฉันไม่เห็นด้วยกับการใช้งานก่อนหน้านี้ ปัญหาคือคุณไม่ควรทำให้บางสิ่งที่ไม่สามารถทำให้แบนเรียบได้ มันสร้างความสับสนและให้ความรู้สึกที่ไม่ถูกต้องของการโต้แย้ง

>>> list(flatten(123))
[123]
>>>

ตัวสร้างต่อไปนี้เกือบจะเหมือนกันกับตัวแรก แต่ไม่มีปัญหาในการพยายามทำให้วัตถุที่ไม่สามารถทำซ้ำได้ มันจะล้มเหลวอย่างที่คาดหวังเมื่อมีการโต้แย้งที่ไม่เหมาะสม

def flatten(iterable):
    for item in iterable:
        try:
            yield from flatten(item)
        except TypeError:
            yield item

การทดสอบเครื่องกำเนิดไฟฟ้าทำงานได้ดีกับรายการที่ให้ไว้ อย่างไรก็ตามรหัสใหม่จะเพิ่มTypeErrorเมื่อวัตถุที่ไม่สามารถทำซ้ำได้จะได้รับมัน ตัวอย่างที่แสดงด้านล่างของพฤติกรรมใหม่

>>> L = [[[1, 2, 3], [4, 5]], 6]
>>> list(flatten(L))
[1, 2, 3, 4, 5, 6]
>>> list(flatten(123))
Traceback (most recent call last):
  File "<pyshell#32>", line 1, in <module>
    list(flatten(123))
  File "<pyshell#27>", line 2, in flatten
    for item in iterable:
TypeError: 'int' object is not iterable
>>>

5

แม้ว่าจะได้เลือกคำตอบที่หรูหราและไพเราะมากฉันจะนำเสนอโซลูชันของฉันสำหรับการตรวจสอบ:

def flat(l):
    ret = []
    for i in l:
        if isinstance(i, list) or isinstance(i, tuple):
            ret.extend(flat(i))
        else:
            ret.append(i)
    return ret

โปรดบอกรหัสนี้ว่าดีหรือไม่ดี


1
isinstance(i, (tuple, list))ใช้ การกำหนดค่าเริ่มต้นให้กับตัวแปรว่างคือการตั้งค่าสถานะให้ฉันมองโครงสร้างรหัสทางเลือกโดยทั่วไปความเข้าใจเครื่องกำเนิดไฟฟ้าการเรียกซ้ำและอื่น ๆ
dansalmo

3
return type(l)(ret)คุณจะได้รับภาชนะประเภทเดียวกันกับที่ผ่านไป :)
dash-tom-bang

@ dash-tom-bang คุณช่วยอธิบายความหมายของรายละเอียดหน่อยได้ไหม
Xolve

1
หากคุณผ่านรายการคุณอาจต้องการกลับรายการ หากคุณส่งผ่านทูเปิลคุณอาจต้องการทูเปิลกลับ หากคุณผ่านการผิดพลาดของทั้งสองคุณจะได้รับสิ่งที่ล้อมรอบสิ่งที่เป็น
dash-tom-bang

4

ฉันชอบคำตอบง่ายๆ ไม่มีเครื่องกำเนิดไฟฟ้า ไม่มีการเรียกซ้ำหรือ จำกัด การเรียกซ้ำ แค่ซ้ำ:

def flatten(TheList):
    listIsNested = True

    while listIsNested:                 #outer loop
        keepChecking = False
        Temp = []

        for element in TheList:         #inner loop
            if isinstance(element,list):
                Temp.extend(element)
                keepChecking = True
            else:
                Temp.append(element)

        listIsNested = keepChecking     #determine if outer loop exits
        TheList = Temp[:]

    return TheList

สิ่งนี้ใช้ได้กับสองรายการ: Inner for loop และ outer while loop

ด้านในสำหรับการวนซ้ำวนซ้ำผ่านรายการ ถ้ามันพบองค์ประกอบของรายการมัน (1) ใช้ list.extend () เพื่อทำให้ส่วนนั้นซ้อนกันในระดับหนึ่งและ (2) จะสลับสวิตช์ไปที่ CheckCheck เป็น True Keepchecking ใช้ในการควบคุมด้านนอกในขณะที่วง หากลูปภายนอกได้รับการตั้งค่าเป็นจริงมันจะทริกเกอร์ลูปภายในสำหรับการผ่านอีกครั้ง

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

รายการที่ถูกทำให้แบนจะถูกส่งคืน

ทดสอบวิ่ง

flatten([1,2,3,4,[100,200,300,[1000,2000,3000]]])

[1, 2, 3, 4, 100, 200, 300, 1000, 2000, 3000]


ฉันชอบเรียบง่ายเช่นกัน ในกรณีนี้คุณซ้ำหลายครั้งในรายการเนื่องจากมีการซ้อนกันหรือระดับ อาจมีราคาแพง
telliott99

@ telliott99: คุณพูดถูกถ้ารายการของคุณใหญ่มากและ / หรือซ้อนกันจนลึกมาก อย่างไรก็ตามหากไม่เป็นเช่นนั้นโซลูชันที่ง่ายกว่าก็ใช้ได้เหมือนกันและไม่มีเวทย์มนตร์ลึกของคำตอบอื่น ๆ มีสถานที่สำหรับความเข้าใจกำเนิดซ้ำหลายขั้นตอน แต่ฉันไม่มั่นใจว่าควรจะเป็นที่ที่คุณมองหาก่อน (ฉันคิดว่าคุณรู้ว่าฉันอยู่ที่ไหนในการอภิปราย "แย่ลงคือดีกว่า")
ดิน

@ telliott99: หรือวิธีอื่นคุณไม่ต้อง "ลองไป Grok" ทางออกของฉัน หากประสิทธิภาพไม่ใช่คอขวดสิ่งสำคัญที่สุดสำหรับคุณในฐานะโปรแกรมเมอร์คืออะไร
ดิน

วิธีที่ง่ายกว่ามีตรรกะน้อยกว่า การเรียกซ้ำเป็นโครงสร้างพื้นฐานของการเขียนโปรแกรมที่ทุกคนที่คิดว่าตัวเองเป็นโปรแกรมเมอร์ควรจะพอใจอย่างสมบูรณ์ เครื่องกำเนิดไฟฟ้าเป็นวิธี Python และ (พร้อมด้วยความเข้าใจ) เป็นสิ่งที่โปรแกรมเมอร์ Python มืออาชีพใด ๆ ควรคลานทันที
dash-tom-bang

1
ฉันเห็นด้วยเกี่ยวกับการเรียกซ้ำ เมื่อฉันเขียนคำตอบของฉันหลามยังคงเรียกซ้ำที่รอบ 1000 พวกเขาเปลี่ยนสิ่งนี้หรือไม่? สำหรับการเป็นโปรแกรมเมอร์งูใหญ่ฉันไม่ใช่ ยิ่งกว่านั้นฉันคิดว่าหลาย ๆ คนที่เขียนโปรแกรมในหลามไม่ทำงานเต็มเวลา
ดิน

4

นี่คือฟังก์ชั่นง่าย ๆ ที่จะทำให้รายการของความลึกโดยพล ไม่มีการเรียกซ้ำเพื่อหลีกเลี่ยงการล้นสแต็ก

from copy import deepcopy

def flatten_list(nested_list):
    """Flatten an arbitrarily nested list, without recursion (to avoid
    stack overflows). Returns a new list, the original list is unchanged.

    >> list(flatten_list([1, 2, 3, [4], [], [[[[[[[[[5]]]]]]]]]]))
    [1, 2, 3, 4, 5]
    >> list(flatten_list([[1, 2], 3]))
    [1, 2, 3]

    """
    nested_list = deepcopy(nested_list)

    while nested_list:
        sublist = nested_list.pop(0)

        if isinstance(sublist, list):
            nested_list = sublist + nested_list
        else:
            yield sublist

ใช่ คล้ายกับรหัสของฉันมากที่github.com/jorgeorpinel/flatten_nested_lists/blob/master/ …
Jorge Orpinel

3

ฉันประหลาดใจที่ไม่มีใครคิดเรื่องนี้ ฉันไม่ได้รับคำตอบซ้ำ ๆ ที่คนขั้นสูงทำ อย่างไรก็ตามนี่คือความพยายามของฉันในเรื่องนี้ ข้อแม้คือมันเฉพาะเจาะจงมากกับกรณีการใช้งานของ OP

import re

L = [[[1, 2, 3], [4, 5]], 6]
flattened_list = re.sub("[\[\]]", "", str(L)).replace(" ", "").split(",")
new_list = list(map(int, flattened_list))
print(new_list)

เอาท์พุท:

[1, 2, 3, 4, 5, 6]

3

ฉันไม่ได้ตอบคำถามทั้งหมดที่มีอยู่แล้วที่นี่ แต่นี่คือหนึ่งในสายการบินที่ฉันคิดขึ้นมายืมมาจากวิธีแรกและรายการส่วนที่เหลือ

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

มันไม่ใช่สายการบินเดียว ว่าเท่าไหร่ที่คุณพยายามที่จะพอดีมันเป็นหนึ่งไม่มีที่def foo():เป็นเส้นที่แยกต่างหาก นอกจากนี้ยังอ่านไม่ได้มาก
cs95

ฉันยกเลิกการใส่รหัสหนึ่งบรรทัดและทำการปรับปรุงใหม่อีกครั้ง (การแก้ไขอยู่ระหว่างการตรวจสอบโดยเพื่อนในขณะที่ฉันเขียน) วิธีการนี้ดูเหมือนว่าจะอ่านได้ง่ายสำหรับฉันแม้ว่ารหัสดั้งเดิมจะต้องมีการปรับโครงสร้างใหม่
Emilio M Bumachar

3

เมื่อพยายามตอบคำถามเช่นนี้คุณจำเป็นต้องให้ข้อ จำกัด ของรหัสที่คุณเสนอเป็นวิธีแก้ปัญหา ถ้ามันเกี่ยวกับการแสดงฉันจะไม่สนใจมากเกินไป แต่รหัสส่วนใหญ่ที่เสนอเป็นวิธีแก้ปัญหา (รวมถึงคำตอบที่ยอมรับได้) ล้มเหลวในการทำให้รายการที่มีความลึกมากกว่า 1,000 แบนเรียบ

เมื่อฉันพูดถึงรหัสส่วนใหญ่ฉันหมายถึงรหัสทั้งหมดที่ใช้การเรียกซ้ำในรูปแบบใด ๆ (หรือเรียกใช้ฟังก์ชันไลบรารีมาตรฐานที่เรียกซ้ำ) รหัสทั้งหมดเหล่านี้ล้มเหลวเนื่องจากการโทรซ้ำแบบเรียกซ้ำทุกครั้งการเรียก (การโทร) จะเพิ่มขึ้นหนึ่งหน่วยและการเรียกแบบหลาม (เริ่มต้น) ของกองมีขนาด 1,000

หากคุณไม่คุ้นเคยกับ call stack มากบางทีสิ่งต่อไปนี้อาจช่วยได้ (ไม่เช่นนั้นคุณก็สามารถเลื่อนไปที่การใช้งาน )

เรียกขนาดสแต็กและการโปรแกรมแบบเรียกซ้ำ

ค้นหาสมบัติและทางออก

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

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

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

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

การเรียกใช้งานโปรแกรมแบบเรียกซ้ำ

โดยทั่วไปมันเป็นสิ่งเดียวกับการค้นหาสมบัติ ดันเจี้ยนเป็นหน่วยความจำของคอมพิวเตอร์เป้าหมายของคุณคือตอนนี้ไม่ได้หาสมบัติ แต่เพื่อคำนวณฟังก์ชั่นบางอย่าง (ค้นหาf (x)สำหรับx ที่กำหนด) ตัวชี้วัดก็ย่อยการปฏิบัติที่จะช่วยให้คุณแก้f (x) กลยุทธ์ของคุณเหมือนกับกลยุทธ์call stack , notebook คือ stack, ห้องเป็นที่อยู่ส่งคืนของฟังก์ชั่น:

x = ["over here", "am", "I"]
y = sorted(x) # You're about to enter a room named `sorted`, note down the current room address here so you can return back: 0x4004f4 (that room address looks weird)
# Seems like you went back from your quest using the return address 0x4004f4
# Let's see what you've collected 
print(' '.join(y))

ปัญหาที่คุณพบในดันเจี้ยนจะเหมือนกันที่นี่สแต็คการโทรมีขนาด จำกัด (ที่นี่ 1,000) ดังนั้นหากคุณป้อนฟังก์ชั่นมากเกินไปโดยไม่ย้อนกลับมาคุณจะเติมสแต็กการโทรและมีข้อผิดพลาด เช่น"นักผจญภัยที่รักฉันเสียใจมาก แต่สมุดบันทึกของคุณเต็ม" :RecursionError: maximum recursion depth exceeded :โปรดทราบว่าคุณไม่จำเป็นต้องเรียกซ้ำเพื่อเติมสแต็คการโทร แต่ก็ไม่น่าเป็นไปได้มากที่โปรแกรม 1000 ที่ไม่เรียกซ้ำจะเรียกใช้ฟังก์ชันโดยไม่ส่งคืน สิ่งสำคัญคือต้องเข้าใจว่าเมื่อคุณกลับมาจากฟังก์ชั่นการเรียกสแต็กจะถูกปลดปล่อยจากที่อยู่ที่ใช้ (เช่นชื่อ "สแต็ค" ที่อยู่ผู้ส่งจะถูกผลักเข้าไปก่อนที่จะเข้าสู่ฟังก์ชั่น ในกรณีพิเศษของการเรียกซ้ำแบบง่าย (ฟังก์ชันfที่เรียกตัวเองว่าครั้งแล้วครั้งเล่า -) คุณจะเข้าfมาซ้ำแล้วซ้ำอีกจนกว่าการคำนวณจะเสร็จสิ้น (จนกว่าจะพบสมบัติ) และกลับจากfจนกว่าคุณจะกลับไปยังสถานที่ที่คุณโทรหาfในตอนแรก สแต็คการโทรจะไม่ถูกทำให้เป็นอิสระจากสิ่งใดจนกว่าจะสิ้นสุดซึ่งจะถูกปลดปล่อยจากที่อยู่ผู้ส่งทั้งหมดหลังจากที่อยู่อีกสายหนึ่ง

จะหลีกเลี่ยงปัญหานี้ได้อย่างไร?

ที่จริงแล้วค่อนข้างเรียบง่าย: "อย่าใช้การเรียกซ้ำถ้าคุณไม่รู้ว่ามันจะไปได้ลึกเพียงใด" ที่ไม่จริงเสมอเช่นในบางกรณีสายหาง recursion สามารถเพิ่มประสิทธิภาพ (TCO) แต่ในไพ ธ อนนี่ไม่ใช่กรณีและแม้แต่ฟังก์ชั่นเรียกซ้ำ "ที่เขียนดี" จะไม่ปรับการใช้สแต็กให้เหมาะสม มีโพสต์ที่น่าสนใจจากกุยโดเกี่ยวกับคำถามนี้: recursion หางกำจัด

มีเทคนิคที่คุณสามารถใช้เพื่อทำการใด ๆ ซ้ำฟังก์ชันเวียนเทคนิคนี้เราอาจจะเรียกเป็นนำโน๊ตบุ๊คของคุณเอง ตัวอย่างเช่นในกรณีของเราโดยเฉพาะเรากำลังสำรวจรายการการเข้าห้องเทียบเท่ากับการป้อนรายการย่อยคำถามที่คุณควรถามตัวเองคือฉันจะกลับจากรายการไปยังรายการหลักได้อย่างไร คำตอบนั้นไม่ซับซ้อนให้ทำซ้ำจนกว่าจะถึงstackจะว่างเปล่า:

  1. กดรายการปัจจุบันaddressและindexในstackเมื่อป้อนรายการย่อยใหม่ (โปรดทราบว่ารายการที่อยู่ + ดัชนียังเป็นที่อยู่ดังนั้นเราจึงใช้เทคนิคแบบเดียวกับที่ใช้โดยสแต็กการโทร)
  2. ทุกครั้งที่พบรายการ yieldมัน (หรือเพิ่มในรายการ);
  3. เมื่อรายการถูกสำรวจอย่างสมบูรณ์แล้วให้กลับไปที่รายการหลักโดยใช้การstack ส่งคืนaddress(และindex) )

นอกจากนี้โปรดทราบว่าสิ่งนี้เทียบเท่ากับ DFS ในทรีที่บางโหนดเป็นรายการย่อยA = [1, 2]และบางรายการเป็นรายการง่าย: 0, 1, 2, 3, 4(สำหรับL = [0, [1,2], 3, 4]) ต้นไม้มีลักษณะเช่นนี้:

                    L
                    |
           -------------------
           |     |     |     |
           0   --A--   3     4
               |   |
               1   2

การสั่งซื้อล่วงหน้าแบบสำรวจเส้นทาง DFS คือ: L, 0, A, 1, 2, 3, 4 โปรดจำไว้ว่าเพื่อใช้ DFS ซ้ำคุณยังต้อง "สแต็ค" อีกครั้ง การดำเนินการที่ฉันเสนอก่อนผลลัพธ์มีสถานะต่อไปนี้ (สำหรับstackและและflat_list):

init.:  stack=[(L, 0)]
**0**:  stack=[(L, 0)],         flat_list=[0]
**A**:  stack=[(L, 1), (A, 0)], flat_list=[0]
**1**:  stack=[(L, 1), (A, 0)], flat_list=[0, 1]
**2**:  stack=[(L, 1), (A, 1)], flat_list=[0, 1, 2]
**3**:  stack=[(L, 2)],         flat_list=[0, 1, 2, 3]
**3**:  stack=[(L, 3)],         flat_list=[0, 1, 2, 3, 4]
return: stack=[],               flat_list=[0, 1, 2, 3, 4]

ในตัวอย่างนี้ขนาดสูงสุดของสแต็กคือ 2 เนื่องจากรายการอินพุต (และต้นไม้) มีความลึก 2

การดำเนินงาน

สำหรับการติดตั้งในไพ ธ อนคุณสามารถลดความซับซ้อนลงเล็กน้อยโดยใช้ตัววนซ้ำแทนรายการอย่างง่าย การอ้างอิงไปยังตัววนซ้ำ (sub) จะถูกใช้เพื่อเก็บรายการที่อยู่ส่งคืนของรายการย่อย (แทนที่จะมีทั้งรายการที่อยู่และดัชนี) นี่ไม่ใช่ความแตกต่างใหญ่ แต่ฉันรู้สึกว่ามันอ่านง่ายกว่า (และเร็วกว่านิดหน่อย):

def flatten(iterable):
    return list(items_from(iterable))

def items_from(iterable):
    cursor_stack = [iter(iterable)]
    while cursor_stack:
        sub_iterable = cursor_stack[-1]
        try:
            item = next(sub_iterable)
        except StopIteration:   # post-order
            cursor_stack.pop()
            continue
        if is_list_like(item):  # pre-order
            cursor_stack.append(iter(item))
        elif item is not None:
            yield item          # in-order

def is_list_like(item):
    return isinstance(item, list)

นอกจากนี้สังเกตว่าในis_list_likeฉันมีisinstance(item, list)ซึ่งสามารถเปลี่ยนเป็นประเภทอินพุตได้มากขึ้นที่นี่ฉันแค่อยากจะมีเวอร์ชั่นที่ง่ายที่สุดโดยที่ (iterable) เป็นเพียงรายการ แต่คุณสามารถทำเช่นนั้นได้:

def is_list_like(item):
    try:
        iter(item)
        return not isinstance(item, str)  # strings are not lists (hmm...) 
    except TypeError:
        return False

นี้จะพิจารณาสตริงเป็นรายการ "ง่าย" และดังนั้นจึงflatten_iter([["test", "a"], "b])จะกลับมาและไม่ได้["test", "a", "b"] ["t", "e", "s", "t", "a", "b"]โปรดทราบว่าในกรณีiter(item)นั้นเรียกว่าสองครั้งในแต่ละรายการลองทำเป็นว่าเป็นการออกกำลังกายสำหรับผู้อ่านเพื่อทำความสะอาดนี้

ทดสอบและพูดถึงการใช้งานอื่น ๆ

ในท้ายที่สุดโปรดจำไว้ว่าคุณไม่สามารถพิมพ์รายการซ้อนแบบไม่ จำกัด ได้Lโดยใช้print(L)เพราะภายในจะใช้การเรียกซ้ำเพื่อ__repr__( RecursionError: maximum recursion depth exceeded while getting the repr of an object) ด้วยเหตุผลเดียวกันการแก้ปัญหาที่flattenเกี่ยวข้องstrจะล้มเหลวพร้อมกับข้อความแสดงข้อผิดพลาดเดียวกัน

หากคุณต้องการทดสอบโซลูชันของคุณคุณสามารถใช้ฟังก์ชันนี้เพื่อสร้างรายการซ้อนแบบง่าย:

def build_deep_list(depth):
    """Returns a list of the form $l_{depth} = [depth-1, l_{depth-1}]$
    with $depth > 1$ and $l_0 = [0]$.
    """
    sub_list = [0]
    for d in range(1, depth):
        sub_list = [d, sub_list]
    return sub_list

ซึ่งจะช่วยให้: >>>build_deep_list(5)[4, [3, [2, [1, [0]]]]]


2

นี่คือการcompiler.ast.flattenใช้งานใน 2.7.5:

def flatten(seq):
    l = []
    for elt in seq:
        t = type(elt)
        if t is tuple or t is list:
            for elt2 in flatten(elt):
                l.append(elt2)
        else:
            l.append(elt)
    return l

มีวิธีที่ดีกว่าและเร็วกว่า (หากคุณมาถึงที่นี่คุณได้เห็นแล้ว)

ทราบด้วย:

เลิกใช้แล้วตั้งแต่เวอร์ชัน 2.6: แพ็คเกจคอมไพเลอร์ถูกลบใน Python 3 แล้ว


2

แฮ็คทั้งหมด แต่ฉันคิดว่ามันจะทำงานได้ (ขึ้นอยู่กับ data_type ของคุณ)

flat_list = ast.literal_eval("[%s]"%re.sub("[\[\]]","",str(the_list)))


1

นี่เป็นอีกวิธีการหนึ่งของ py2 ฉันไม่แน่ใจว่ามันเร็วที่สุดหรือหรูหราที่สุดหรือปลอดภัยที่สุด ...

from collections import Iterable
from itertools import imap, repeat, chain


def flat(seqs, ignore=(int, long, float, basestring)):
    return repeat(seqs, 1) if any(imap(isinstance, repeat(seqs), ignore)) or not isinstance(seqs, Iterable) else chain.from_iterable(imap(flat, seqs))

มันสามารถเพิกเฉยชนิดใด ๆ (หรือที่ได้รับ) ประเภทใดก็ได้ที่คุณต้องการมันจะคืนค่าตัววนซ้ำดังนั้นคุณสามารถแปลงมันเป็นคอนเทนเนอร์เฉพาะเช่นรายการ tuple, dict หรือเพียงแค่ใช้มันเพื่อลดขนาดหน่วยความจำ มันสามารถจัดการวัตถุที่ไม่ใช่ iterable เริ่มต้นเช่น int ...

โปรดสังเกตว่าการยกของหนักส่วนใหญ่จะทำใน C เนื่องจากเท่าที่ฉันรู้ว่ามันใช้งานอย่างไรดังนั้นในขณะที่มันเกิดซ้ำ AFAIK ไม่ได้ถูก จำกัด โดยความลึกของการเรียกซ้ำของงูใหญ่เนื่องจากการเรียกใช้ฟังก์ชันเกิดขึ้นใน C ไม่ได้หมายความว่าคุณถูก จำกัด ด้วยหน่วยความจำโดยเฉพาะอย่างยิ่งใน OS X ที่ขนาดสแต็กมีขีด จำกัด สูง ณ วันนี้ (OS X Mavericks) ...

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

def flat(seqs, ignore={int, long, float, str, unicode}):
    return repeat(seqs, 1) if type(seqs) in ignore or not isinstance(seqs, Iterable) else chain.from_iterable(imap(flat, seqs))

ที่นี่เราใช้ชุดตรวจสอบประเภทดังนั้นมันต้องใช้ O (1) เทียบกับ O (จำนวนประเภท) เพื่อตรวจสอบว่าองค์ประกอบควรถูกละเว้นหรือไม่ แต่แน่นอนว่าค่าใด ๆ ที่มีประเภทที่ถูกละเว้นประเภทที่ระบุจะล้มเหลว นี่คือเหตุผลของการใช้str,unicodeเพื่อใช้ด้วยความระมัดระวัง ...

การทดสอบ:

import random

def test_flat(test_size=2000):
    def increase_depth(value, depth=1):
        for func in xrange(depth):
            value = repeat(value, 1)
        return value

    def random_sub_chaining(nested_values):
        for values in nested_values:
            yield chain((values,), chain.from_iterable(imap(next, repeat(nested_values, random.randint(1, 10)))))

    expected_values = zip(xrange(test_size), imap(str, xrange(test_size)))
    nested_values = random_sub_chaining((increase_depth(value, depth) for depth, value in enumerate(expected_values)))
    assert not any(imap(cmp, chain.from_iterable(expected_values), flat(chain(((),), nested_values, ((),)))))

>>> test_flat()
>>> list(flat([[[1, 2, 3], [4, 5]], 6]))
[1, 2, 3, 4, 5, 6]
>>>  

$ uname -a
Darwin Samys-MacBook-Pro.local 13.3.0 Darwin Kernel Version 13.3.0: Tue Jun  3 21:27:35 PDT 2014; root:xnu-2422.110.17~1/RELEASE_X86_64 x86_64
$ python --version
Python 2.7.5

1

โดยไม่ต้องใช้ห้องสมุดใด ๆ :

def flat(l):
    def _flat(l, r):    
        if type(l) is not list:
            r.append(l)
        else:
            for i in l:
                r = r + flat(i)
        return r
    return _flat(l, [])



# example
test = [[1], [[2]], [3], [['a','b','c'] , [['z','x','y']], ['d','f','g']], 4]    
print flat(test) # prints [1, 2, 3, 'a', 'b', 'c', 'z', 'x', 'y', 'd', 'f', 'g', 4]

1

การใช้itertools.chain:

import itertools
from collections import Iterable

def list_flatten(lst):
    flat_lst = []
    for item in itertools.chain(lst):
        if isinstance(item, Iterable):
            item = list_flatten(item)
            flat_lst.extend(item)
        else:
            flat_lst.append(item)
    return flat_lst

หรือไม่ผูกมัด:

def flatten(q, final):
    if not q:
        return
    if isinstance(q, list):
        if not isinstance(q[0], list):
            final.append(q[0])
        else:
            flatten(q[0], final)
        flatten(q[1:], final)
    else:
        final.append(q)

1

ฉันใช้ recursive เพื่อแก้ไขรายการที่ซ้อนอยู่ด้วยความลึกใด ๆ

def combine_nlist(nlist,init=0,combiner=lambda x,y: x+y):
    '''
    apply function: combiner to a nested list element by element(treated as flatten list)
    '''
    current_value=init
    for each_item in nlist:
        if isinstance(each_item,list):
            current_value =combine_nlist(each_item,current_value,combiner)
        else:
            current_value = combiner(current_value,each_item)
    return current_value

ดังนั้นหลังจากที่ฉันนิยามฟังก์ชั่น include_nlist มันเป็นเรื่องง่ายที่จะใช้ฟังก์ชั่นนี้ทำการแบน หรือคุณสามารถรวมมันเป็นฟังก์ชั่นเดียว ฉันชอบโซลูชันของฉันเพราะสามารถใช้กับรายการที่ซ้อนอยู่

def flatten_nlist(nlist):
    return combine_nlist(nlist,[],lambda x,y:x+[y])

ผลลัพธ์

In [379]: flatten_nlist([1,2,3,[4,5],[6],[[[7],8],9],10])
Out[379]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

"รายการที่ซ้อนด้วยความลึกใด ๆ " ไม่เป็นความจริง ลองดูสิ: current_value = combiner(current_value,each_item) RecursionError: maximum recursion depth exceeded
cglacet

อืมฉันกำลังพยายามทำให้รายชื่อแบนที่มีเลเยอร์มากกว่า 1,000 เลเยอร์หรือไม่?
Oldyoung

แน่นอนว่าเป็นจุดรวมของการอภิปรายเกี่ยวกับโซลูชันแบบวนซ้ำและแบบวนซ้ำ หากคุณทราบล่วงหน้าว่าจำนวนเลเยอร์น้อยกว่า 1,000 วิธีการแก้ปัญหาที่ง่ายที่สุดจะทำงานได้ เมื่อคุณพูดว่า "ความลึกใด ๆ " จะรวมรายการที่มีความลึก> 1,000 ด้วย
cglacet

1

วิธีที่ง่ายที่สุดคือการใช้Morphpip install morphห้องสมุดใช้

รหัสคือ:

import morph

list = [[[1, 2, 3], [4, 5]], 6]
flattened_list = morph.flatten(list)  # returns [1, 2, 3, 4, 5, 6]

1

ฉันรู้ว่ามีคำตอบที่ยอดเยี่ยมอยู่แล้ว แต่ฉันต้องการเพิ่มคำตอบที่ใช้วิธีการเขียนโปรแกรมการทำงานของการแก้คำถาม ในคำตอบนี้ฉันใช้ประโยชน์จากการเรียกซ้ำสองครั้ง:

def flatten_list(seq):
    if not seq:
        return []
    elif isinstance(seq[0],list):
        return (flatten_list(seq[0])+flatten_list(seq[1:]))
    else:
        return [seq[0]]+flatten_list(seq[1:])

print(flatten_list([1,2,[3,[4],5],[6,7]]))

เอาท์พุท:

[1, 2, 3, 4, 5, 6, 7]

1

ฉันไม่แน่ใจว่าจำเป็นต้องเร็วกว่าหรือมีประสิทธิภาพมากกว่านี้หรือไม่ แต่นี่คือสิ่งที่ฉันทำ:

def flatten(lst):
    return eval('[' + str(lst).replace('[', '').replace(']', '') + ']')

L = [[[1, 2, 3], [4, 5]], 6]
print(flatten(L))

flattenฟังก์ชั่นที่นี่จะเปิดรายการเป็นสตริง, จะออกทั้งหมดของวงเล็บแนบวงเล็บกลับเข้าสู่ปลายและเปลี่ยนมันกลับเข้ามาในรายการ

แม้ว่าถ้าคุณรู้ว่าคุณจะมีวงเล็บเหลี่ยมในรายการของคุณในสายเช่น[[1, 2], "[3, 4] and [5]"]คุณจะต้องทำอย่างอื่น


สิ่งนี้ไม่มีข้อได้เปรียบเหนือวิธีแก้ปัญหาอย่างง่ายเนื่องจากไม่สามารถประมวลผลรายการที่ลึกได้เช่น "RecursionError: ความลึกการเรียกซ้ำสูงสุดเกินขณะรับการตอบสนองของวัตถุ"
cglacet

1

นี่เป็นการนำแบนราบ ๆ ไปใช้บน python2

flatten=lambda l: reduce(lambda x,y:x+y,map(flatten,l),[]) if isinstance(l,list) else [l]

test=[[1,2,3,[3,4,5],[6,7,[8,9,[10,[11,[12,13,14]]]]]],]
print flatten(test)

#output [1, 2, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]

1

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

def flatten_obj(n_obj, key=True, my_sep=''):
    my_string = ''
    if type(n_obj) == list:
        for val in n_obj:
            my_sep_setter = my_sep if my_string != '' else ''
            if type(val) == list or type(val) == dict:
                my_string += my_sep_setter + flatten_obj(val, key, my_sep)
            else:
                my_string += my_sep_setter + val
    elif type(n_obj) == dict:
        for k, v in n_obj.items():
            my_sep_setter = my_sep if my_string != '' else ''
            d_val = k if key else v
            if type(v) == list or type(v) == dict:
                my_string += my_sep_setter + flatten_obj(v, key, my_sep)
            else:
                my_string += my_sep_setter + d_val
    elif type(n_obj) == str:
        my_sep_setter = my_sep if my_string != '' else ''
        my_string += my_sep_setter + n_obj
        return my_string
    return my_string

print(flatten_obj(['just', 'a', ['test', 'to', 'try'], 'right', 'now', ['or', 'later', 'today'],
                [{'dictionary_test': 'test'}, {'dictionary_test_two': 'later_today'}, 'my power is 9000']], my_sep=', ')

อัตราผลตอบแทน:

just, a, test, to, try, right, now, or, later, today, dictionary_test, dictionary_test_two, my power is 9000

0

หากคุณชอบการเรียกซ้ำนี่อาจเป็นวิธีแก้ปัญหาที่คุณสนใจ:

def f(E):
    if E==[]: 
        return []
    elif type(E) != list: 
        return [E]
    else:
        a = f(E[0])
        b = f(E[1:])
        a.extend(b)
        return a

จริง ๆ แล้วฉันดัดแปลงมันจากรหัสโครงการแบบฝึกหัดที่ฉันเขียนไปสักพัก

สนุก!


0

ฉันใหม่กับงูหลามและมาจากพื้นหลังเสียงกระเพื่อม นี่คือสิ่งที่ฉันเกิดขึ้น (ตรวจสอบชื่อ var สำหรับ lulz):

def flatten(lst):
    if lst:
        car,*cdr=lst
        if isinstance(car,(list,tuple)):
            if cdr: return flatten(car) + flatten(cdr)
            return flatten(car)
        if cdr: return [car] + flatten(cdr)
        return [car]

ดูเหมือนว่าจะทำงาน ทดสอบ:

flatten((1,2,3,(4,5,6,(7,8,(((1,2)))))))

ผลตอบแทน:

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