จะตรวจสอบว่าองค์ประกอบทั้งหมดของรายการตรงกับเงื่อนไขได้อย่างไร?


208

ฉันมีรายการที่ประกอบไปด้วย 20,000 รายการ ฉันใช้องค์ประกอบที่ 3 ของแต่ละรายการเป็นค่าสถานะ ฉันต้องการที่จะดำเนินการบางอย่างในรายการนี้ตราบใดที่ธงอย่างน้อยหนึ่งองค์ประกอบเป็น 0 มันเหมือน:

my_list = [["a", "b", 0], ["c", "d", 0], ["e", "f", 0], .....]

ในการเริ่มต้นการตั้งค่าสถานะทั้งหมดเป็น 0 ฉันใช้การวนรอบสักครู่เพื่อตรวจสอบว่าค่าสถานะอย่างน้อยหนึ่งองค์ประกอบเป็น 0:

def check(list_):
    for item in list_:
        if item[2] == 0:
            return True
    return False

ถ้าcheck(my_list)กลับTrueมาฉันจะทำงานต่อในรายการของฉัน:

while check(my_list):
    for item in my_list:
        if condition:
            item[2] = 1
        else:
            do_sth()

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

my_list ดั้งเดิมไม่มีธง:

my_list = [["a", "b"], ["c", "d"], ["e", "f"], .....]

เนื่องจากฉันไม่สามารถลบองค์ประกอบได้ในขณะที่วนซ้ำฉันจึงคิดค้นธงเหล่านี้ แต่ในนั้นmy_listมีหลายไอเท็มและwhileลูปจะอ่านมันทั้งหมดในแต่ละforลูปและมันกินเวลามาก! คุณมีข้อเสนอแนะใด?


3
ดูเหมือนว่าโครงสร้างข้อมูลของคุณจะไม่เหมาะกับปัญหาของคุณ หากคุณอธิบายบริบทเพิ่มเติมอีกเล็กน้อยเราอาจแนะนำสิ่งที่เหมาะสมกว่า
uselpa

บางทีคุณสามารถแทนที่รายการด้วยNoneหรือ[]ในขณะที่คุณวนซ้ำรายการแทนการลบ การตรวจสอบรายการทั้งหมดด้วย 'check () `วนซ้ำรายการทั้งหมดก่อนการผ่านแต่ละรอบบนวงในเป็นวิธีที่ช้ามาก
martineau

คำตอบ:


402

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

>>> items = [[1, 2, 0], [1, 2, 0], [1, 2, 0]]
>>> all(flag == 0 for (_, _, flag) in items)
True
>>> items = [[1, 2, 0], [1, 2, 1], [1, 2, 0]]
>>> all(flag == 0 for (_, _, flag) in items)
False

โปรดทราบว่าall(flag == 0 for (_, _, flag) in items)เทียบเท่าโดยตรงกับall(item[2] == 0 for item in items)มันเป็นเพียงเล็กน้อยดีกว่าที่จะอ่านในกรณีนี้

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

>>> [x for x in items if x[2] == 0]
[[1, 2, 0], [1, 2, 0]]

หากคุณต้องการตรวจสอบอย่างน้อยหนึ่งองค์ประกอบคือ 0 ตัวเลือกที่ดีกว่าคือการใช้any()ซึ่งสามารถอ่านได้มากขึ้น:

>>> any(flag == 0 for (_, _, flag) in items)
True

ความผิดของฉันในการใช้แลมบ์ดา, Python ทั้งหมดไม่ยอมรับฟังก์ชั่นเป็นอาร์กิวเมนต์แรกเช่น Haskell et al. ฉันเปลี่ยนคำตอบเป็นรายการเข้าใจเช่นกัน :)
Hampus Nilsson

3
@HampusNilsson ความเข้าใจในรายการไม่เหมือนกับนิพจน์ตัวสร้าง ในฐานะที่เป็นall()และany()ลัดวงจรถ้ายกตัวอย่างเช่นค่าแรกในเหมืองประเมินFalse, all()จะล้มเหลวและไม่ได้ตรวจสอบค่าใด ๆ Falseเพิ่มเติมกลับ ตัวอย่างของคุณจะทำเช่นเดียวกันยกเว้นว่าจะสร้างรายการเปรียบเทียบทั้งหมดก่อนซึ่งหมายถึงการประมวลผลจำนวนมากโดยไม่ทำอะไรเลย
Gareth Latty

14

หากคุณต้องการตรวจสอบว่ามีรายการใดในรายการที่ละเมิดเงื่อนไขการใช้งานหรือไม่all:

if all([x[2] == 0 for x in lista]):
    # Will run if all elements in the list has x[2] = 0 (use not to invert if necessary)

หากต้องการลบองค์ประกอบทั้งหมดที่ไม่ตรงกันให้ใช้ filter

# Will remove all elements where x[2] is 0
listb = filter(lambda x: x[2] != 0, listb)

2
คุณสามารถลบ[...]ในall(...)ตั้งแต่มันก็สามารถสร้างเครื่องกำเนิดไฟฟ้าแทนของรายการซึ่งไม่เพียง แต่ช่วยให้คุณประหยัดตัวละครสองตัว แต่ยังช่วยประหยัดเวลาและหน่วยความจำ โดยการใช้เครื่องกำเนิดไฟฟ้าจะมีการคำนวณรายการเดียวเท่านั้นในแต่ละครั้ง (ผลลัพธ์ในอดีตจะถูกทิ้งตั้งแต่ไม่ได้ใช้) และหากมีรายการใดปรากฎออกมาFalseเครื่องกำเนิดจะหยุดการคำนวณส่วนที่เหลือ
InQβ

7

คุณสามารถใช้ tertewhile ของ itertools ได้เช่นนี้มันจะหยุดทันทีที่พบเงื่อนไขที่ทำให้คำสั่งของคุณล้มเหลว วิธีตรงกันข้ามจะลดลง

for x in itertools.takewhile(lambda x: x[2] == 0, list)
    print x

0

itertools.ifilterวิธีการใช้งานอื่น ๆ ตรวจสอบความจริงและกระบวนการ (ใช้lambda)

ตัวอย่าง-

for x in itertools.ifilter(lambda x: x[2] == 0, my_list):
    print x

0

วิธีนี้ยืดหยุ่นกว่าการใช้เล็กน้อยall():

my_list = [[1, 2, 0], [1, 2, 0], [1, 2, 0]]
all_zeros = False if False in [x[2] == 0 for x in my_list] else True
any_zeros = True if True in [x[2] == 0 for x in my_list] else False

หรือมากกว่ารัดกุม:

all_zeros = not False in [x[2] == 0 for x in my_list]
any_zeros = 0 in [x[2] for x in my_list]

คุณไม่สามารถพูดall_zeros = False in [x[2] == 0 for x in my_list]หรือแม้กระทั่ง0 in [x[2] for x in my_list]และสอดคล้องกันเพื่อany_zeros? ฉันไม่เห็นการปรับปรุงใด ๆ all()ในช่วงที่น่าทึ่ง
tripleee

ไม่เวอร์ชันของคุณall_zeros = False in [x[2] == 0 for x in my_list]จะประเมินถึงFalseในขณะที่การประเมินของฉันจะTrueเป็น หากคุณเปลี่ยนเป็นall_zeros = not (False in [x[2] == 0 for x in my_list])มันก็เท่ากับของฉัน และจะเห็นได้ชัดเพียงจะทำงานให้0 in [x[2] for x in my_list] any_zerosแต่ฉันชอบความกระชับของความคิดของคุณดังนั้นฉันจะอัปเดตคำตอบของฉัน
mulllhausen
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.