pairwise traversal of a list or tuple

后端 未结 6 1368
走了就别回头了
走了就别回头了 2020-12-03 14:11
a = [5, 66, 7, 8, 9, ...]

Is it possible to make an iteration instead of writing like this?

a[1] - a[0]

a[2] - a[1]

a[3] - a[2]

         


        
相关标签:
6条回答
  • 2020-12-03 14:15

    It's ok to use range. However, programming (like maths) is about building on abstractions. Consecutive pairs [(x0, x1), (x1, x2), ..., (xn-2, xn-1)], are called pairwise combinations. See an example in the itertools docs. Once you have this function in your toolset, you can write:

    for x, y in pairwise(xs):
        print(y - x)
    

    Or used as a generator expression:

    consecutive_diffs = (y - x for (x, y) in pairwise(xs))
    
    0 讨论(0)
  • 2020-12-03 14:16

    for a small list in python 2 or any list in python 3, you can use

    [x - y for x, y in zip(a[1:], a)]
    

    for a larger list, you probably want

    import itertools as it
    
    [x - y for x, y in it.izip(a[1:], a)]
    

    if you are using python 2

    And I would consider writing it as a generator expression instead

    (x - y for x, y in it.izip(a[1:], a))
    

    This will avoid creating the second list in memory all at once but you will only be able to iterate over it once. If you only want to iterate over it once, then this is ideal and it's easy enough to change if you decide later that you need random or repeated access. In particular if you were going to further process it to make a list, then this last option is ideal.

    update:

    The fastest method by far is

    import itertools as it
    import operator as op
    
    list(it.starmap(op.sub, it.izip(a[1:], a)))
    

    $ python -mtimeit -s's = [1, 2]*10000' '[x - y for x, y in zip(s[1:], s)]'
    100 loops, best of 3: 13.5 msec per loop
    
    $ python -mtimeit -s'import itertools as it; s = [1, 2]*10000' '[x - y for x, y in it.izip(s[1:], s)]'
    100 loops, best of 3: 8.4 msec per loop
    
    $ python -mtimeit -s'import itertools as it; import operator as op; s = [1, 2]*10000' 'list(it.starmap(op.sub, it.izip(s[1:], s)))'
    100 loops, best of 3: 6.38 msec per loop
    
    0 讨论(0)
  • 2020-12-03 14:21
    def pairwise(iterable):
        i = iter(iterable)
        while True:
            yield next(i), next(i, '')
    
    0 讨论(0)
  • 2020-12-03 14:25

    Here is the example from the itertools reciepes:

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

    Which is not very readable.If you prefer something more understandable and understand how generators work, here a bit longer example with the same result:

    def pairwise(it):
        """
        Walk a list in overlapping pairs.
    
        >>> list(pairwise([0, 1, 2, 3, 4, 5]))
        [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]
        """
        it = iter(it)
        start = None
        while True:
            if not start:
                start = next(it)
            end = next(it)
            yield start, end
            start = end
    
    0 讨论(0)
  • 2020-12-03 14:29

    I would recommend to use awesome more_itertools library, it has ready-to-use pairwise function:

    import more_itertools
    
    for a, b in more_itertools.pairwise([1, 2, 3, 4, 5]):
        print(a, b)
    # 1 2
    # 2 3
    # 3 4
    # 4 5
    

    It will save you from writing your own (likely buggy) implementation. For example, most of implementations on this page don't handle the case with empty iterable correctly -- generator function should never raise StopIteration, this behavior considered deprecated and causes DeprecationWarning in Python 3.6. It won't work in Python 3.7 at all.

    0 讨论(0)
  • 2020-12-03 14:38

    Sure.

    for i in range(1, len(a)):
        print a[i] - a[i-1]
    

    I fail to see what the real problem is here. Have you read the python tutorial?

    0 讨论(0)
提交回复
热议问题