【问题标题】:Python for loops and the "sum13" methodPython for 循环和“sum13”方法
【发布时间】:2017-09-21 23:42:32
【问题描述】:

我是一个本地 C++ 程序员,刚开始学习 python,我得到了以下方法来编写:

返回数组中数字的总和,对于空数组返回 0。除了数字 13 很不吉利,所以它不算数,紧跟在 13 之后的数字也不算数。

我的解决办法是:

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

熟悉其他基于 C 语言的人会发现这里的循环类似于(相当干净)

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

请注意,我几天前才开始学习 Python,所以我对这门语言还是很陌生。我希望有人可以就如何以“Python”方式编写此循环提供一些建议,或者使用我可能不知道的一些语言功能提供更清洁的解决方案。


在我之前的尝试中(没有成功)我有:

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'

【问题讨论】:

  • 看看for在python中可用的循环:docs.python.org/3/tutorial/controlflow.html
  • 这确实更适合Code Review
  • @aryamccarthy 我什至不知道它的存在。我很抱歉。
  • 不需要。祝你好运。
  • @StephenRauch 是的,此代码有效。发帖人正在寻找使其更加 Pythonic 的方法。

标签: python for-loop methods indexing while-loop


【解决方案1】:
def sum13(nums):
    sum = 0
    if len(nums) > 0 and nums[0] != 13:
        sum = nums[0]

    for i in range(1, len(nums)):
        if nums[i] != 13 and nums[i - 1] != 13:
            sum += nums[i]
    return sum


print(sum13([1, 2, 2, 1]))  # → 6
print(sum13([1, 1]))  # → 2
print(sum13([1, 2, 2, 1, 13]))  # → 6

【讨论】:

    【解决方案2】:

    以下是我认为在 Python 习语中的脚本中的解决方案。我添加了一些测试数据。

    为什么问题中的 for 循环不起作用

    在 C 或 C++ 中的 for 循环中修改索引是可以的,因为 C 实际上重写了这个:

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

    这样:

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

    Python 不这样做。像for INDEX in LIST 这样的Python 代码每次迭代都从LIST 中获取一个值,并将其分配给INDEX。因此,一旦到达循环顶部,在循环内修改INDEX 就没有效果。 (一个细节点是LIST 不一定是列表;它可以是产生列表的东西,例如迭代器或生成器。)

    使用 zip 的解决方案

    TOTH @stephen-rauch 用于 zip 解决方案,我对其进行了一些更改。

    • [0] + data 动态创建一个完整的列表,这会消耗内存。 在下面的代码中,它被替换为生成器。
    • i for i, j in ... if 13 not in (i, j) 解包返回的二元组 zip,然后将其重新打包成一个二元组以进行 in 测试。在代码中 下面,整个元组被分配并在没有拆包的情况下进行测试,然后 第一个元素是在列表推导之外提取的。

    代码提取

    我编写了一个包含五个解决方案的脚本,其中一个显示与问题中的循环相同的错误。 zip 解决方案是最好的,所以我从答案中省略了其他解决方案。

    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)
    

    输出

    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
    

    编码愉快!

    【讨论】:

      【解决方案3】:

      您可以使用与 while 循环相同的想法,并使用如下代码保存一些簿记(我已对其进行了修改以连续处理多个 13):

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

      这个单线也应该可以正常工作:

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

      下一个比较平淡,有点混乱,但可行(因为你评论了才留在这里):

      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
      

      或者,最后是一个漂亮、简单、明确的版本:

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

      【讨论】:

      • 我真的很喜欢你的第一个解决方案。对我来说,你的一个班轮似乎是 StephenRauch 提供的更复杂的版本
      • @AldenB,我相信这个在 [1,13, 13, 2, 1] 情况下也有问题,可能是长版本和单线产生不同的结果。
      • 这看起来与我在这之前的一个问题惊人地相似(可怕的格式提前):返回数组中数字的总和,除了忽略以 6 开头并扩展到下一个 7(每 6 个后面至少有一个 7)。如果没有数字,则返回 0。 def sum67(nums): result = 0 count = True for i in range(len(nums)): if nums[i] == 6: count = False if count: result += nums[i] if nums[i] == 7: count = True 返回结果
      • @AldenB,自从您开始评论它以来,这已经发生了很大变化。不确定您是否会再喜欢第一个版本,但希望其中仍有一些有用的东西!
      【解决方案4】:

      只是想在(显式)循环之外思考......

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

      如果eval(...) 困扰您,请替换为sum(map(int, (...).split('+')))

      【讨论】:

        【解决方案5】:

        您可以在正常的 for 循环中执行此操作:

        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
        

        【讨论】:

        • 这是一个不错的算法,但你的实现有问题——你有一个形式参数nums,它应该是temp,它应该返回而不是打印。
        • 对 sumarate([1,2,13]) 失败
        • @cdlane 我知道它应该返回,但我做了一个打印来展示它是如何工作的。而temp 应该是nums 的论点是不对的。变量的名称不需要等于函数/方法中的参数名称。在每个使用函数/方法的地方调用都非常草率,就像在函数/方法中调用它一样。
        • @Smart Manoj ou,我忘记了这种测试。谢谢:)
        • 再看看你的代码。您传递了nums,但您在全局temp 上操作并忽略nums
        【解决方案6】:

        sumzip 理解如何:

        代码:

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

        结果:

        27
        

        这是如何工作的?

        从内到外,我们从zip 开始。 zip 接受多个迭代,并在第一次迭代时返回每个迭代的第一个元素,然后在第二次迭代中返回每个迭代的第二个元素,等等。

        所以我们要评估数据的当前元素,加上前一个元素,所以我们传递数据,并通过在前面填充数据(在本例中为0)将数据偏移一个元素

        这两个列表被展开,一次一个元素,到i, j。然后,作为一种理解,如果13 不在i, j 中,我们返回i。然后由sum 评估理解,这很奇怪,它汇总了所有返回的元素。

        【讨论】:

        • 您可以通过删除括号来避免创建整个列表。
        • 这是一个绝妙的解决方案。谢谢你!
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-07-15
        • 1970-01-01
        • 1970-01-01
        • 2022-06-28
        • 1970-01-01
        相关资源
        最近更新 更多