วนซ้ำทุกองค์ประกอบในรายการ


209

ฉันจะทำให้forวนซ้ำหรือความเข้าใจในรายการเพื่อให้ทุกการวนซ้ำให้ฉันสององค์ประกอบ?

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

for i,k in ???:
    print str(i), '+', str(k), '=', str(i+k)

เอาท์พุท:

1+2=3
3+4=7
5+6=11

2
สำหรับคู่ที่ทับซ้อนกัน: stackoverflow.com/questions/5434891/…
user202729

คำตอบ:


231

คุณต้องมีการติดตั้งpairwise()(หรือgrouped())

สำหรับ Python 2:

from itertools import izip

def pairwise(iterable):
    "s -> (s0, s1), (s2, s3), (s4, s5), ..."
    a = iter(iterable)
    return izip(a, a)

for x, y in pairwise(l):
   print "%d + %d = %d" % (x, y, x + y)

หรือโดยทั่วไปแล้ว:

from itertools import izip

def grouped(iterable, n):
    "s -> (s0,s1,s2,...sn-1), (sn,sn+1,sn+2,...s2n-1), (s2n,s2n+1,s2n+2,...s3n-1), ..."
    return izip(*[iter(iterable)]*n)

for x, y in grouped(l, 2):
   print "%d + %d = %d" % (x, y, x + y)

ในหลาม 3 คุณสามารถแทนที่izipด้วยในตัวฟังก์ชั่นและวางzip()import

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

หมายเหตุ : สิ่งนี้ไม่ควรสับสนกับpairwiseสูตรในitertoolsเอกสารของ Python ซึ่งให้ผลs -> (s0, s1), (s1, s2), (s2, s3), ...ตามที่@lazyrชี้ให้เห็นในความคิดเห็น

นอกจากนี้เล็กน้อยสำหรับผู้ที่ต้องการพิมพ์การตรวจสอบกับmypyใน Python 3:

from typing import Iterable, Tuple, TypeVar

T = TypeVar("T")

def grouped(iterable: Iterable[T], n=2) -> Iterable[Tuple[T, ...]]:
    """s -> (s0,s1,s2,...sn-1), (sn,sn+1,sn+2,...s2n-1), ..."""
    return zip(*[iter(iterable)] * n)

16
เพื่อไม่ให้สับสนกับฟังก์ชั่นการจับคู่ที่แนะนำในส่วนสูตรitertoolsซึ่งให้ผลผลิตs -> (s0,s1), (s1,s2), (s2, s3), ...
Lauritz V. Thaulow

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

ฮะ? ฟังก์ชั่นของคุณและฟังก์ชั่นที่ฉันอ้างถึงทำในสิ่งที่แตกต่างกันและนั่นคือประเด็นของความคิดเห็นของฉัน
Lauritz V. Thaulow

5
ระวัง! การใช้ฟังก์ชั่นเหล่านี้ทำให้คุณเสี่ยงต่อการไม่วนซ้ำองค์ประกอบสุดท้ายของการทำซ้ำ ตัวอย่าง: รายการ (จัดกลุ่ม ([1,2,3], 2)) >>> [(1, 2)] .. เมื่อคุณคาดหวัง [(1,2), (3,)]
egafni

4
@ Erik49: ในกรณีที่ระบุไว้ในคำถามมันไม่มีเหตุผลที่จะมี tuple 'ไม่สมบูรณ์' หากคุณต้องการที่จะรวม tuple ที่ไม่สมบูรณ์คุณสามารถใช้แทนizip_longest() izip()เช่น: ->list(izip_longest(*[iter([1, 2, 3])]*2, fillvalue=0)) [(1, 2), (3, 0)]หวังว่านี่จะช่วยได้
Johnsyweb

191

ทีนี้คุณต้องมี tuple 2 องค์ประกอบดังนั้น

data = [1,2,3,4,5,6]
for i,k in zip(data[0::2], data[1::2]):
    print str(i), '+', str(k), '=', str(i+k)

ที่ไหน:

  • data[0::2] หมายถึงการสร้างชุดย่อยขององค์ประกอบที่ (index % 2 == 0)
  • zip(x,y) สร้างคอลเลกชัน tuple จาก x และ y คอลเลกชันองค์ประกอบดัชนีเดียวกัน

8
สามารถขยายได้ในกรณีที่ต้องการองค์ประกอบมากกว่าสององค์ประกอบ สำหรับเช่นfor i, j, k in zip(data[0::3], data[1::3], data[2::3]):
สมดุลของชีวิต

19
สะอาดกว่าการดึงในการนำเข้าและกำหนดฟังก์ชั่น!
kmarsh

7
@kmarsh: แต่นี้ใช้งานได้เฉพาะในลำดับฟังก์ชั่นการทำงานบน iterable ใด ๆ และสิ่งนี้ใช้พื้นที่พิเศษ O (N) ฟังก์ชั่นไม่ได้; ในทางกลับกันนี่จะเร็วกว่าปกติ มีเหตุผลที่ดีในการเลือกข้อใดข้อหนึ่ง ความกลัวimportไม่ใช่หนึ่งในนั้น
abarnert

77
>>> l = [1,2,3,4,5,6]

>>> zip(l,l[1:])
[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]

>>> zip(l,l[1:])[::2]
[(1, 2), (3, 4), (5, 6)]

>>> [a+b for a,b in zip(l,l[1:])[::2]]
[3, 7, 11]

>>> ["%d + %d = %d" % (a,b,a+b) for a,b in zip(l,l[1:])[::2]]
['1 + 2 = 3', '3 + 4 = 7', '5 + 6 = 11']

1
สิ่งนี้ไม่ได้ทำงานกับ Python-3.6.0 แต่ยังคงทำงานกับ Python-2.7.10
Hamid Rohani

6
@HamidRohani zipส่งคืนzipวัตถุใน Python 3 ซึ่งไม่สามารถถอดได้ มันจะต้องถูกแปลงเป็นลำดับ ( list, tupleฯลฯ ) ก่อน แต่"ไม่ทำงาน"นั้นค่อนข้างยืด
vaultah

58

ทางออกที่ง่าย

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

สำหรับฉันอยู่ในช่วง (0, len (l), 2):
    พิมพ์ str (l [i]), '+', str (l [i + 1]), '=', str (l [i] + l [i + 1])

1
เกิดอะไรขึ้นถ้ารายการของคุณไม่เท่ากันและคุณต้องการเพียงแสดงหมายเลขสุดท้ายตามที่เป็นอยู่?
Hans de Jong

@HansdeJong ไม่ได้รับคุณ โปรดอธิบายเพิ่มเติมอีกหน่อย
taskinoor

2
ขอบคุณ ฉันคิดแล้วว่าจะทำอย่างไร ปัญหาคือถ้าคุณมีรายการที่ไม่มีจำนวนแม้แต่จำนวนในนั้นมันจะได้รับข้อผิดพลาดของดัชนี แก้ไขด้วยความพยายามยกเว้น:
Hans de Jong

หรือ((l[i], l[i+1])for i in range(0, len(l), 2))สำหรับเครื่องกำเนิดไฟฟ้าสามารถแก้ไขได้ง่ายสำหรับ tuples ที่ยาวขึ้น
Basel Shishani

44

ในขณะที่คำตอบทั้งหมดที่ใช้zipนั้นถูกต้อง แต่ฉันพบว่าการใช้งานฟังก์ชั่นของคุณเองทำให้รหัสอ่านง่ายขึ้น

def pairwise(it):
    it = iter(it)
    while True:
        try:
            yield next(it), next(it)
        except StopIteration:
            # no more elements in the iterator
            return

it = iter(it)ส่วนหนึ่งเพื่อให้แน่ใจว่าitเป็นจริง iterator, ไม่ได้เป็นเพียง iterable หากitเป็นตัววนซ้ำแล้วบรรทัดนี้จะไม่มีตัวเลือก

การใช้งาน:

for a, b in pairwise([0, 1, 2, 3, 4, 5]):
    print(a + b)

2
วิธีการแก้ปัญหานี้ช่วยให้การวางนัยทั่วไปขนาดของทูเปิล> 2
guilloptero

1
วิธีนี้ยังใช้งานได้หากitเป็นตัววนซ้ำเท่านั้นและไม่สามารถวนซ้ำได้ โซลูชันอื่น ๆ ดูเหมือนจะขึ้นอยู่กับความเป็นไปได้ในการสร้างตัววนซ้ำสองตัวสำหรับลำดับ
Skyking

ฉันพบวิธีการนี้ที่stackoverflow.com/a/16815056/2480481ก่อนดูคำตอบนี้ สะอาดกว่าง่ายกว่าจัดการกับ zip ()
m3nda

2
ฉันชอบที่จะช่วยให้หลีกเลี่ยงการใช้หน่วยความจำสามเท่าเป็นคำตอบที่ยอมรับได้
Kentzo

นี้ไม่ได้ทำงานอย่างกับforลูปในหลาม 3.5+ เนื่องจากPEP 479ซึ่งแทนที่ใด ๆยกขึ้นในเครื่องกำเนิดไฟฟ้ากับที่StopIteration RuntimeError
ซิดนีย์


18

ในกรณีที่คุณสนใจในประสิทธิภาพฉันได้สร้างเกณฑ์มาตรฐานขนาดเล็ก (ใช้ห้องสมุดของฉันsimple_benchmark) เพื่อเปรียบเทียบประสิทธิภาพของโซลูชันและฉันได้รวมฟังก์ชันจากหนึ่งในแพ็คเกจของฉัน:iteration_utilities.grouper

from iteration_utilities import grouper
import matplotlib as mpl
from simple_benchmark import BenchmarkBuilder

bench = BenchmarkBuilder()

@bench.add_function()
def Johnsyweb(l):
    def pairwise(iterable):
        "s -> (s0, s1), (s2, s3), (s4, s5), ..."
        a = iter(iterable)
        return zip(a, a)

    for x, y in pairwise(l):
        pass

@bench.add_function()
def Margus(data):
    for i, k in zip(data[0::2], data[1::2]):
        pass

@bench.add_function()
def pyanon(l):
    list(zip(l,l[1:]))[::2]

@bench.add_function()
def taskinoor(l):
    for i in range(0, len(l), 2):
        l[i], l[i+1]

@bench.add_function()
def mic_e(it):
    def pairwise(it):
        it = iter(it)
        while True:
            try:
                yield next(it), next(it)
            except StopIteration:
                return

    for a, b in pairwise(it):
        pass

@bench.add_function()
def MSeifert(it):
    for item1, item2 in grouper(it, 2):
        pass

bench.use_random_lists_as_arguments(sizes=[2**i for i in range(1, 20)])
benchmark_result = bench.run()
mpl.rcParams['figure.figsize'] = (8, 10)
benchmark_result.plot_both(relative_to=MSeifert)

ป้อนคำอธิบายรูปภาพที่นี่

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

หากคุณไม่ทราบพึ่งพาเพิ่มเติมแล้วgrouperจากiteration_utilitiesอาจจะบิตเร็วขึ้น

ความคิดเพิ่มเติม

วิธีการบางอย่างมีข้อ จำกัด บางอย่างที่ไม่ได้กล่าวถึงในที่นี้

ตัวอย่างโซลูชันที่ใช้งานได้เฉพาะกับลำดับ (นั่นคือรายการสตริง ฯลฯ ) ตัวอย่างเช่นโซลูชัน Margus / pyanon / taskinoor ซึ่งใช้การจัดทำดัชนีในขณะที่โซลูชันอื่นทำงานบน iterable ใด ๆ (นั่นคือลำดับและตัวกำเนิด, ตัววนซ้ำ) เช่น Johnysweb / mic_e / โซลูชั่นของฉัน

Johnysweb ยังมีวิธีแก้ปัญหาที่ใช้งานได้กับขนาดอื่นที่ไม่ใช่ 2 ในขณะที่คำตอบอื่น ๆ ไม่ได้ (โอเคการiteration_utilities.grouperตั้งค่าจำนวนองค์ประกอบเป็น "กลุ่ม")

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

เมื่อgrouperคุณสามารถตัดสินใจได้ว่าต้องการทำอะไร:

>>> from iteration_utilities import grouper

>>> list(grouper([1, 2, 3], 2))  # as single
[(1, 2), (3,)]

>>> list(grouper([1, 2, 3], 2, truncate=True))  # ignored
[(1, 2)]

>>> list(grouper([1, 2, 3], 2, fillvalue=None))  # padded
[(1, 2), (3, None)]

12

ใช้คำสั่งzipและiterด้วยกัน:

ฉันพบว่าโซลูชันนี้ใช้งานiterได้ค่อนข้างหรูหรา:

it = iter(l)
list(zip(it, it))
# [(1, 2), (3, 4), (5, 6)]

ซึ่งผมพบในเอกสารซิปหลาม 3

it = iter(l)
print(*(f'{u} + {v} = {u+v}' for u, v in zip(it, it)), sep='\n')

# 1 + 2 = 3
# 3 + 4 = 7
# 5 + 6 = 11

ในการพูดคุยกับNองค์ประกอบในแต่ละครั้ง:

N = 2
list(zip(*([iter(l)] * N)))
# [(1, 2), (3, 4), (5, 6)]

10
for (i, k) in zip(l[::2], l[1::2]):
    print i, "+", k, "=", i+k

zip(*iterable) ส่งคืน tuple ด้วยอิลิเมนต์ถัดไปของแต่ละ iterable

l[::2] ส่งกลับองค์ประกอบที่ 1, 3, 5 และอื่น ๆ ของรายการ: เครื่องหมายโคลอนแรกระบุว่าชิ้นเริ่มต้นที่จุดเริ่มต้นเนื่องจากไม่มีหมายเลขอยู่ด้านหลังเครื่องหมายโคลอนที่สองจำเป็นเฉพาะเมื่อคุณต้องการ 'ขั้นตอนในชิ้น '(ในกรณีนี้ 2)

l[1::2]ทำสิ่งเดียวกัน แต่เริ่มต้นในองค์ประกอบที่สองของรายการดังนั้นจึงส่งกลับองค์ประกอบที่ 2, 4, 6, ฯลฯ ของรายการต้นฉบับ


4
คำตอบนี้ได้รับโดย Margus เมื่อสองปีก่อน stackoverflow.com/questions/5389507/…
cababunga

1
1 เพื่ออธิบายวิธีการ[number::number]ทำงานของไวยากรณ์ มีประโยชน์สำหรับผู้ที่ไม่ใช้งูใหญ่บ่อยครั้ง
Alby

2

ด้วยการเปิดออก:

l = [1,2,3,4,5,6]
while l:
    i, k, *l = l
    print(str(i), '+', str(k), '=', str(i+k))

ว้าว! ทำไมฉันถึงคิดเรื่องนี้ไม่ได้ :) คุณต้องจัดการคดีที่ไม่มีคู่
แท้

1

สำหรับทุกคนที่อาจช่วยได้นี่คือวิธีแก้ปัญหาที่คล้ายกัน แต่มีคู่ที่ทับซ้อนกัน (แทนที่จะเป็นคู่ที่ไม่เกิดร่วมกัน)

จากเอกสารประกอบของ Python itertools :

from itertools import izip

def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return izip(a, b)

หรือโดยทั่วไปแล้ว:

from itertools import izip

def groupwise(iterable, n=2):
    "s -> (s0,s1,...,sn-1), (s1,s2,...,sn), (s2,s3,...,sn+1), ..."
    t = tee(iterable, n)
    for i in range(1, n):
        for j in range(0, i):
            next(t[i], None)
    return izip(*t)


1

ฉันต้องการหารรายการด้วยตัวเลขและคงที่เช่นนี้

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

def divideByN(data, n):
        return [data[i*n : (i+1)*n] for i in range(len(data)//n)]  

>>> print(divideByN(l,2))
[[1, 2], [3, 4], [5, 6]]

>>> print(divideByN(l,3))
[[1, 2, 3], [4, 5, 6]]

1

มีหลายวิธีที่จะทำเช่นนั้น ตัวอย่างเช่น:

lst = [1,2,3,4,5,6]
[(lst[i], lst[i+1]) for i,_ in enumerate(lst[:-1])]    
>>>[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]

[i for i in zip(*[iter(lst)]*2)]    
>>>[(1, 2), (3, 4), (5, 6)]

0

คิดว่านี่เป็นสถานที่ที่ดีในการแบ่งปันภาพรวมของสิ่งนี้กับ n> 2 ซึ่งเป็นเพียงหน้าต่างบานเลื่อนเหนือ iterable:

def sliding_window(iterable, n):
    its = [ itertools.islice(iter, i, None) 
            for i, iter
            in enumerate(itertools.tee(iterable, n)) ]                               

    return itertools.izip(*its)

0

ชื่อของคำถามนี้ทำให้เข้าใจผิดดูเหมือนว่าคุณกำลังมองหาคู่ที่ต่อเนื่องกัน แต่ถ้าคุณต้องการที่จะทำซ้ำในชุดของคู่ที่เป็นไปได้ทั้งหมดกว่านี้จะทำงาน:

for i,v in enumerate(items[:-1]):
        for u in items[i+1:]:

0

การใช้การพิมพ์เพื่อให้คุณสามารถตรวจสอบข้อมูลโดยใช้เครื่องมือการวิเคราะห์แบบคงที่mypy :

from typing import Iterator, Any, Iterable, TypeVar, Tuple

T_ = TypeVar('T_')
Pairs_Iter = Iterator[Tuple[T_, T_]]

def legs(iterable: Iterator[T_]) -> Pairs_Iter:
    begin = next(iterable)
    for end in iterable:
        yield begin, end
        begin = end

0

วิธีการง่าย ๆ :

[(a[i],a[i+1]) for i in range(0,len(a),2)]

สิ่งนี้มีประโยชน์หากอาร์เรย์ของคุณคือ a และคุณต้องการวนซ้ำโดยใช้คู่ หากต้องการทำซ้ำสามหรือมากกว่านั้นเพียงแค่เปลี่ยนคำสั่งขั้นตอน "range" ตัวอย่างเช่น:

[(a[i],a[i+1],a[i+2]) for i in range(0,len(a),3)]

(คุณต้องจัดการกับค่าส่วนเกินถ้าความยาวของอาเรย์และขั้นตอนไม่พอดี)


-1

ที่นี่เราสามารถมีalt_elemวิธีการที่เหมาะกับคุณสำหรับวง

def alt_elem(list, index=2):
    for i, elem in enumerate(list, start=1):
        if not i % index:
           yield tuple(list[i-index:i])


a = range(10)
for index in [2, 3, 4]:
    print("With index: {0}".format(index))
    for i in alt_elem(a, index):
       print(i)

เอาท์พุท:

With index: 2
(0, 1)
(2, 3)
(4, 5)
(6, 7)
(8, 9)
With index: 3
(0, 1, 2)
(3, 4, 5)
(6, 7, 8)
With index: 4
(0, 1, 2, 3)
(4, 5, 6, 7)

หมายเหตุ: วิธีการแก้ปัญหาข้างต้นอาจไม่มีประสิทธิภาพในการพิจารณาดำเนินการใน func


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