Python for loops and the "sum13" method

I am a C ++ programmer just starting to learn python and I was given the following way:

Returns the sum of the numbers in an array, returning 0 for an empty array. Except for the number 13, it is very unfortunate, so it is not counted and the numbers that appear immediately after 13 are also not counted.

My decision:

def sum13(nums):
  elem_sum = 0
  index = 0

  while index < len(nums):
    if nums[index] != 13:
      elem_sum += nums[index]
    else:
      index += 1 #nums[i] is 13, so skip the next element'
    index += 1

  return elem_sum

      

Those familiar with other C based languages ​​will find a loop here similar to (pretty clean)

for(int i = 0; i < nums.size() /*nums.length*/; ++i) {
    if(nums[i] != 13) elem_sum += nums[i];
    else i++;
}

      

Please note that I started learning Python just a few days ago, so I am still very new to the language. I hope someone can offer some advice on how to write this "Python" loop, or perhaps suggest a cleaner solution using some language features that I might not be aware of.


In my previous attempt (which didn't work) I had:

for i in range(len(nums)):
    if nums[i] != 13:
      elem_sum += nums[i]
    else:
      i += 1 #nums[i] is 13, so skip the next element'

      

+3


source to share


5 answers


How about understanding with sum

and zip

:

Code:

data = list(range(12, 16))
print(sum(i for i, j in zip(data, [0] + data) if 13 not in (i, j)))

      

Results:

27

      



How it works?

Working from the inside out, we start with zip

. zip takes multiple iterations and returns the first element of each iterable on the first iteration, and then the second element of each on the second iteration, etc.

So, we want to evaluate the current item plus the previous item, so we pass the data, and the data is offset by one item, filling the data in front (in this case, a 0

)

These two lists expand, one item at a time, to i, j

. Then, as an understanding, we return i

if 13

it is not in any of the i, j

. The comprehension is then evaluated with a help sum

that, oddly enough, sums up all the returned items.

+5


source



You can do it in a normal loop:



def sumarate(temp):
     my_sum = 0
     for index, value in enumerate(temp):
            if value == 13 or index!=0 and temp[index - 1] == 13:
                continue
            else:
                 my_sum += value
     print my_sum

>>>temp = [13,1,1,1,13]
>>>sumarate(temp)
2
>>> temp = [13, 1, 1, 2]
>>> sumarate(temp)
3
>>> temp = [13, 1, 1, 2, 13]
>>> sumarate(temp)
3
>>> temp = [1, 13, 1, 1, 2]
>>> sumarate(temp)
4

      

0


source


You can use the same idea as a while loop and keep some accounting records with code like the following (I revised it to handle multiple 13 lines):

elem_sum = 0
it = iter(nums)
for n in it:
    if n == 13:
        # skip any additional 13 plus the next number after them
        while next(it, 0) == 13:
            pass
    else:
        elem_sum += n

      

This one line line should work fine as well:

elem_sum = sum(
    n for (i, n) in enumerate(nums) 
    if n != 13 and (i == 0 or nums[i-1] != 13)
)

      

The next one is pretty pedestrian and a little confusing, but workable (left here just because you commented it out):

elem_sum = 0
was_13 = False
for n in nums:
    if n == 13:
        was_13 = True
    elif was_13:
        was_13 = False
    else:
        elem_sum += n

      

Or finally a nice, simple, explicit version:

elem_sum = 0
prev_num = 0
for num in nums:
    if num != 13 and prev_num != 13:
        elem_sum += num
    prev_num = num

      

0


source


Just trying to think outside of a (explicit) loop ...

import re

def sum13(numbers):
    return eval(re.sub(r'(?:\+13)+(?:\+\d+)?', '', '+'.join(map(str, [0, *numbers]))))

      

If eval(...)

worries you, replace it sum(map(int, (...).split('+')))

instead.

0


source


Below is the solution, which I think is in the Python idiom, in a script. I have added some test data.

Why is the for loop in the question not working

Changing the index in a for loop in C or C ++ looks like OK, because C actually overwrites this:

for(A;B;C) {D;}

      

like this:

A; while(B) {D; C;}

      

Python doesn't do this. Python code such as for INDEX in LIST

takes a value from LIST

each iteration and assigns it INDEX

. Therefore, the change INDEX

within the cycle is not affected as soon as the top of the cycle is reached. (The detail point LIST

doesn't have to be a list; it can be something that creates a list, such as an iterator or generator.)

Solution using zip

TOTH @ stephen-rauch for a zip solution to which I made a couple of changes.

  • [0] + data

    creates a whole list on the fly that will consume memory. The code below replaces it with a generator.
  • i for i, j in ... if 13 not in (i, j)

    unpacks the two tuples returned by zip and then repackages it into two tuples for testing in

    . In the code below, the entire tuple is assigned and tested without unpacking, then the first element is retrieved outside of the list comprehension.

Extracting the code

I wrote a script for five solutions, including one that shows the same error as the loop in the question. The zip solution is the best, so I've omitted the rest from the answer.

def sum_lucky_zip(list, unlucky=13):
    """
    Zip of the list and a generator. Best approach and in the Python idiom.
    """
    def list_plus_one(list, unlucky):
        yield unlucky + 1
        for item in list:
            yield item
    return sum(items[0] for items in zip(list, list_plus_one(list, unlucky))
               if unlucky not in items)

def test_sum(summer, lists):
    print(summer.__name__)
    for list in lists:
        print(list, summer(list))
    print()

if __name__ == '__main__':
    testLists = (
        (),
        (13,),
        (1, 2, 4),
        (1, 2, 13, 4),
        (13, 1, 2),
        (13, 13),
        (1, 2, 13, 4, 13, 8),
        (13, 13, 1, 2),
        tuple(range(12, 16)))
    test_sum(sum_lucky_zip, testLists)

      

Output

sum_lucky_zip
() 0
(13,) 0
(1, 2, 4) 7
(1, 2, 13, 4) 3
(13, 1, 2) 2
(13, 13) 0
(1, 2, 13, 4, 13, 8) 3
(13, 13, 1, 2) 2
(12, 13, 14, 15) 27

      

Happy coding!

0


source







All Articles