【问题标题】:How to make a for-loop more understandable in python? [closed]如何在 python 中使 for 循环更易于理解? [关闭]
【发布时间】:2019-09-26 04:33:52
【问题描述】:

我目前正在向一些相当年轻的学生教授一些 Python 编程。我希望他们学习的一件事是如何编写 for 循环。

到目前为止,我给学生们展示的方式是这样的:

for i in range(1,11):
    print(i)

它给出了一个循环,i 从 1 变为 10。

我的问题是,当学生希望循环增加到 10 时,他们需要将 11 作为第二个参数写入 range(1,11),这对学生来说似乎很奇怪。学生们觉得这很混乱。

在C/C++及相关语言中,这样的循环可以这样写:

for(int i = 1; i <= 10; i++)
    { /* do something */ }

在我看来,表达循环的 C++ 方式更直观,因为在这种情况下,我可以明确写出 1 和 10,它们是我希望循环变量采用的第一个和最后一个值。

在 python 中使用 for 循环时,我最终会告诉学生类似 “我们只需要接受当我们希望循环转到 10 时需要写 11,这有点烦人但您只需要了解 range 函数就是以这种方式工作的”。我对此不满意;我希望他们知道编程很有趣,我担心这种事情会降低它的乐趣。

由于 python 经常被描述为一种强调可读性的语言,我怀疑有一种更好的方式来表达 for 循环,这种方式可以减少我的学生的困惑。

有没有更好和/或更少混淆的方式来用 python 语言表达这种 for 循环?

【问题讨论】:

  • 你为什么不简单解释一下 range() 函数呢?上限(函数的第二个参数)不包含在生成的数字中,因为它是上限;该函数生成的数字达到此限制。
  • 不仅仅是range() - Python 在任何地方都使用这样的半开区间。有关原因的讨论,请参阅 quora.com/…。
  • 我已经用 C 语言编程了 30 多年,而且我很少像您那样编写 for 循环。通常我会使用循环变量作为数组索引,这意味着它必须从零开始。同理,我经常写一个python for循环为for n in range(10),循环十次。如果您从该表达式而不是range(1,11) 成语开始,这个概念会更容易理解。不要教你的学生使用 0 作为列表索引是一种奇怪的迷信。多做几次就很自然了。
  • 很多人在这里也提到了,@Paul 在 cmets 中也是如此。只需使用 range(10) 但解释它从零开始。如果要打印数字 1 到 10,只需使用 print(i + 1)。您仍然必须解释基于零的性质,但使用 range(10) 然后 range(1,11) 更符合逻辑 编辑:也永远不要说“只是因为它是”之类的话。这是有原因的,试着解释一下。
  • 我建议使用您自己的数组而不是使用范围。因为看起来您正试图避免基于零的计数的混淆。本课的目标是教授 for 循环;只关注那个。引入范围会增加课程的复杂性(取决于孩子的年龄)。

标签: python loops


【解决方案1】:

您可以向他们展示这段代码以便更好地理解:

start = 1
length = 10
for i in range(start,start+length):
    print(i)

还有另一个类似这样工作的功能,称为slice。

【讨论】:

  • 我认为这是一个选择。也许首先将学生介绍给for i in [1,2,3]:(及其变体),然后问他们在哪里这个数字列表开始和多久它是。将[1,2,3] 转移到range(1,1+3) 应该很有意义,我想。
【解决方案2】:

提醒他们 range 函数以这种方式工作是有原因的。它的一个有用属性是循环运行的次数等于 range 的第二个参数减去第一个参数。

我认为人们对此很感兴趣,但事实上 Python 中的 for 循环与 C 中的循环非常不同。在 C 中,for 循环基本上是一个 while 循环的包装器。

这两个例子应该有助于展示循环在 C 和 python 中的工作方式之间的区别。

# for(int x=1; x <= 10; x++)
x = 1
while x <= 10:
    print(x)
    x += 1


i = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]  # range(1, 11)
for x in i:
    print(i)

但老实说,这里真正的问题是,如果所有循环和数组从零开始,而不是从一开始,它们就更容易理解和使用。请考虑调整您的示例以从零开始。

这样,如果要循环 10 次,就使用数字 10。

   # for(int x=0; x < 10; x++)
x = 0
while x < 10:
    print(x)
    x += 1


i = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]  # range(10)
for x in i:
    print(i)

【讨论】:

  • 我认为将循环与另一种编程语言进行比较可能不是教年轻学生的理想方式(如问题中所述)。
【解决方案3】:

我相信有两种简单的方法可以回答这个问题。 1)解释这个答案的一种方法是使用数学符号半闭区间[a,b)。在此间隔中,包括一个端点(在此示例中为 'a' ),但不包括另一个端点('b')。 所以对于你的例子,

for i in range(1,11):
     print(i)

(1,11) 是半闭区间,其中 a 和 b 分别为 1 和 11。

2) 你也可以用下面的例子来解释

    for i in range(1,11)  //in python 
        {do something}

    for(int i=1;i<11;i++)  //in C++
        {do something}

在这两种情况下,i 从 1 迭代到 10。这对我来说似乎更直观。

【讨论】:

  • 我真的很喜欢对已经了解区间的人(科学家、数学家)的“半开区间”解释。对于那些不熟悉区间的人,我发现我最终会花费大量时间来解释区间是什么。
  • 我完全同意你的担心。您可以在link 中阅读。对不起,迟到的帖子。对数学不是很了解的,可以用第二种解释来理解。
【解决方案4】:

向他们展示两个 C++ 变体:

# using <= operator
for(int i = 1; i <= 10; i++)
    { /* do something */ }

# using < operator
for(int i = 1; i < 11; i++)
    { /* do something */ }

并告诉他们python的range函数像第二个一样运作。

【讨论】:

    【解决方案5】:

    我的问题是,当学生希望循环增加到 10 时,他们需要将 11 作为 range(1,11) 的第二个参数写成 11,这对学生来说似乎很奇怪。学生们觉得这很混乱。

    混淆不是来自for 语句,而是来自range。您需要做的是将您的解释分为两部分:首先是for 迭代它的论点,而不关心论点的内容。所以

    for i in [1,2,3]:
        {*do something*}
    

    用 3 个元素迭代列表。

    现在,range 被定义为整数的半开区间 a &lt;= x &lt; b,因为它有许多很好的数学性质,比如

    len(range(a, b)) == b - a
    len(range(a)) == a
    range(a, a) == []
    range(a, b) + range(b, c) == range(a, c)
    

    如果range 的定义是一个闭区间,程序员就不得不在这里和那里用-1 进行调整。

    This 是一篇不错的关于主题的博文。

    【讨论】:

      【解决方案6】:

      您的学生将受益于尽早了解大多数编程语言都使用从零开始的索引。理解 Python 的 range() 函数最简单的方法是只使用一个参数。

      for i in range(10):
          # will iterate exactly 10 times with i going from 0 to 9
      

      在一个从零开始的世界中,这是完全合理的。

      Python 的范围(以区间表示法表示)有一个独占上限:[0..[10,而不是非计算机工程师默认使用的包含区间 [1...10]

      鉴于此,赋予范围函数的参数被理解为独占的“停止”值。

      当你使用一个额外的参数来提供一个起点时,突然不同地对待停止值是不一致的。这会使 range(0,10) 的行为与 range(10) 不同,这将非常令人困惑。

      与 C 的主要区别在于 range() 表示“小于”比较而不是“小于或等于”:

       for i in range(A,B):   # for (i=A;i<B;i++)
           ...
      

      您可以为您的学生提供“inclusiveRange”功能,让他们在不掌握从零开始的概念的情况下开始学习。

       def inclusiveRange(start=0,end,step=1):
           return range(start,end+1,step)
      

      他们可以使用它来代替 range()

       for i in inclusiveRange(1,10):
           ... # will iterate from 1 to 10 inclusively
      

      缺点是当学生开始使用列表索引或计算坐标系中的位置时,他们将不得不“忘记”循环。通过今天让他们加入从零开始的索引,您将来可以为他们节省很多 - 1 和 off by one。

      【讨论】:

        【解决方案7】:

        这不是“更多”或“更少”可以理解的,而是关于你如何描述它:

        • for x in something:将可迭代 something 中的值作为变量 x 进行迭代。所以你需要解释“range(...)返回什么”。
        • range(start, end,...) 返回一个对象,该对象产生从开始(包括)到停止(不包括)的整数序列。给他们看help(range)

        而你问的是“如何让 for 循环更像 C 语言?”

        有一些方法可以做到这一点:

        
        def nrange(start, num_elements, step=1):
            """Similar to `range`, but second argument is number of elements."""
            return range(start, start + step*num_elements, step)
        
        
        def inclusive_range(start_or_stop, stop=None, step=1):
            if stop is None:
                start = 0
                stop = start_or_stop+1
            else:
                start = start_or_stop
                stop = stop + step
            return range(start, stop, step)
        
        
        _default_update_func = lambda item: item+1
        def c_like_iterator(start, test_func, update_func=_default_update_func):
            cur_value = start
            while test_func(cur_value):
                yield cur_value
                cur_value = update_func(cur_value)
        
        for i in nrange(1, 10):
            print(i)
        
        for i in inclusive_range(1, 10):
            print(i)
        
        for i in inclusive_range(10):  # this will give 11 elements because both 0 and 10 values are included
            print(i)
        
        for i in c_like_iterator(1, lambda x: x<=10, lambda x: x+1):
            print(i)
        
        for i in c_like_iterator(1, lambda x: x<11, lambda x: x+1):
            print(i)
        
        for i in inclusive_range(1, -10, -1):
            print(i)
        
        

        【讨论】:

          【解决方案8】:

          试试这个:

          for i in range(10):
              print(i+1)
          

          或

          list_of_numbers = range(1,11)
          
          for number in list_of_numbers:
              print(number)
          

          【讨论】:

            【解决方案9】:

            让他们理解list和array总是从0开始的标准方式很重要。为了更容易理解,给他们看下面的代码,这将很容易理解为什么数组从索引开始0. 不要避免复杂性,而是要简化它。

            for i in range(10):
                i+=1
                print(i)
            

            或

            for i in range(10):
                print(i+1)
            

            【讨论】:

              【解决方案10】:

              问题不在于理解 for 循环,而在于理解不包括端点的范围。一种看待它的方法是将范围的一部分视为栅栏柱和段;用 range(10) 建造一个“栅栏”会给你一个有 10 个栅栏的栅栏帖子 == |

              |-|-|-|-|-|-|-|

              但是,您看到的数字计算围栏段 == - 您必须在围栏柱 (|) 的左侧,即 0-1-2-3-4-5 -6-7-8-9

              如果您添加一个起点 range(1,10),它仍然会构建一个包含 10 个栅栏柱的栅栏,但您的索引只查看具有至少 1 个栅栏段的所有内容,因此第一个栅栏柱不是您的一部分范围。但是,当您查看的第一个栅栏帖子的索引从 0 开始时,您会得到 1-2-3-4-5-6-7-8-9;仍然没有 10,因为我们原来的栅栏只有 10 个柱子长。所以要么你用 11 个柱子建造一个栅栏,然后忽略少于 1 个段(范围(1,11))的所有东西,或者你用 10 个柱子建造一个栅栏,假装它左边还有另一个段(范围(0,10 )+1)。

              将步长更改为大于 1 仅意味着您只计算每个 2nd / 3rd / etc 栅栏柱,从第一个开始,所以如果剩下更多栅栏柱但没有更多的栅栏柱可以计算,那么最后的其余部分是切断并忽略。

              【讨论】:

                【解决方案11】:

                我认为计算机科学学习中最重要的概念之一是了解数组索引的工作原理。你应该花一些时间用简单的例子清楚地向学生解释这个概念。自 LISP 以来,几乎所有编程语言都以 Zero-based numbering 开头。 Python 和 C++ 没有什么不同。在 Python 中,可以使用简单的lists 作为示例来说明概念。

                #a simple list
                In [15]: list1 = [1,2,3,4,5,6,7,8,9,10,11]
                In [23]: len(list1) #number of items in the list
                Out[23]: 11
                In [26]: list1[0] #the first item in the list
                Out[26]: 1
                In [25]: print("list1 starts at index {}. The number at this index is {}.".format(list1.index(1), list1[0]))
                list1 starts at index 0. The number at this index is 1.
                In [37]: list1[10] #the last item in the list
                Out[37]: 11
                In [19]: print("list1 ends at index {}. The number at this index is {}.".format(len(list1)-1, list1[-1]))
                list1 ends at index 10. The number at this index is 11.
                

                如您所见,“数字”值比“索引”值早1。如果列表 list1 以 0 开头并以 11 结尾,那么索引值和“数字”值将相同,但列表中的项目数将增加 1,这是因为我们包含了 0。暂时保持这个想法,因为我们需要这个:

                In [29]: list2 = [0,1,2,3,4,5,6,7,8,9,10,11]
                In [31]: len(list2) #total number of items in list
                Out[31]: 12
                In [32]: list2[0]
                Out[32]: 0
                In [35]: list2[11]
                Out[35]: 11
                

                请记住,根据文档,range() 的语法是:range(start, end, step)。我们将忽略此讨论的步骤。

                所以,现在如果我想使用range() 生成与list2 类似的列表,我可以使用上述信息来形成 range() 的通用语法:

                Syntax: range(start, len(list2)+start), where start is the first item in the range, 
                        list2 is a list and len(list2) is its size and the end is len(list2) + start
                

                要将其放入列表中,我只需将上述内容作为参数传递给list() 函数。深入了解此功能的本质并不重要,仅用于说明。因此,我们得到:

                In [39]: list3 = list(range(0,len(list2)+0))    
                In [40]: list3
                Out[40]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]    
                In [41]: list2 == list3 #both lists are equivalent
                Out[41]: True
                In [42]: len(list3)
                Out[42]: 12          #same number of items as list2
                In [46]: for i in range(0,len(list3)+0): #can replace list3 with list2 to get same results
                    ...:     print(i, end=' ')
                        ...:
                
                0 1 2 3 4 5 6 7 8 9 10 11   #Output         
                

                for 循环按索引位置进行迭代。因此,i 从第 0 项的索引位置 0(范围开始)开始,一直到第 11 项的索引位置 11(范围结束 = 11 + 0)。

                我们还可以验证list1 的上述语法,其中 start 为 1,长度为 11。

                In [6]: list(range(1,len(list1)+1))
                Out[6]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] #same as list1 above
                In [15]: for i in range(1,len(list1)+1):
                    ...:     print(i, end=' ')
                    ...:
                1 2 3 4 5 6 7 8 9 10 11
                

                同样,for 循环像往常一样从索引位置 0 开始,其中包含项目 1(范围开始),并在索引位置 10 结束,其中包含项目 11(范围结束 = 11 + 1)。

                现在,让我们看看这个概念如何也适用于 C++。我们使用相同的list2,它有 12 个项目,即len(list2) = 12。同样,如果学生对数组不太熟悉,那么深入了解以下示例中的所有细节并不重要。这仅用于说明目的:

                #include <iostream>
                
                using namespace std;
                
                int main()
                {
                    int list2[12] = {0,1,2,3,4,5,6,7,8,9,10,11};
                    std::cout << "Length of array list2 = " << (sizeof(list2)/sizeof(*list2)) << std::endl;
                    for(int i=0;i<(sizeof(list2)/sizeof(*list2));i++)
                    {
                        cout<<i<<' ';
                    }
                    return 0;
                }
                
                //Output:
                Length of array list2 = 12                                                                                            
                0 1 2 3 4 5 6 7 8 9 10 11  #Output
                

                注意i&lt;(sizeof(list2)/sizeof(*list2)) 确保从数组的上限检索最后一项,即索引=12-1=11 的第 11 项。这正是range() 函数的作用。如果我执行i&lt;=(sizeof(list2)/sizeof(*list2)),那也会在输出中打印 12,这不是list2 中的项目,并且会超出数组的上限。在 Python 中,range() 函数对数组边界更加明确。因此,如果我覆盖上面的 range() 语法并允许 for 循环通过将 end 递增 1 从 0 迭代到 list2 之外的项目,我可以显式打印该项目:

                In [13]: for i in range(0,len(list2)+1):
                    ...:     if i>=len(list2):
                    ...:         print("\nI am outside of list2: {}".format(i))
                    ...:     else:
                    ...:         print(i, end=' ')
                    ...:
                0 1 2 3 4 5 6 7 8 9 10 11
                I am outside of list2: 12
                

                我们从这次讨论中了解到,将 for 循环迭代理解为基于索引的操作很重要,range(start,end) 是 Python 内置函数,它仅在开始和结束之间组合值 范围,包括起始值,不包括结束值。考虑遍历的值的总数,包括 start(len(total) + start) 作为范围的 end 处的项目。 for 循环的实现与此无关,只考虑 Python 和 C++ 中的索引位置。

                【讨论】:

                  【解决方案12】:

                  如here 所述,Python for 循环的运行方式与传统 C 风格的 for 循环不同。它们的功能更像是其他语言中的“for each”循环或迭代器方法。您的示例中使用的 range 函数只是一种创建整数对象集合以进行迭代的便捷方法。包含“开始”而不包含“停止”的原因总结得很好here。

                  在向新程序员解释这一点时,我会分解它并解释 for 循环是如何工作的,然后解释如何使用 range 函数为 for 循环提供一组迭代。

                  示例: for 循环用于为集合中的每个项目执行以下代码块:

                  collection = [1, 2, 3]
                  for collectionMember in collection:
                      print(collectionMember)
                  

                  接下来解释如何使用 range 函数创建整数集合。

                  创建可用于循环的整数集合的一种简单方法是使用 range() 函数:

                  collection = range(1,4)
                  print(collection)
                  for collectionMember in collection:
                     print(collectionMember)
                  

                  我知道这不是 for 循环在其他语言中的工作方式,但在我看来,如果你教他们用 python 编程,首先教他们 python 版本如何工作,然后一旦他们牢牢掌握,会更容易在那解释循环在其他语言中是如何工作的,以及它是如何不同的。

                  【讨论】:

                    【解决方案13】:

                    我猜孩子们已经上小学了,教学时要记住的是保持简单。一次给他们一点点信息。
                    你想教他们循环吗? 只教他们关于 for 循环的知识。

                    假设他们知道字符串和列表可能会使用这种类型的示例:

                    somelist = ['a', 'b', 'c', 'd', 'e']
                    for letter in somelist:
                        print(letter)
                    

                    【讨论】:

                      【解决方案14】:

                      通过阅读您的帖子。我认为一切都不能一概而论。分开的事情有自己的方式。 编程概念相同,但在不同的语言中,它们的表示或语法不同。 所以,如果我是你,我会分享这个概念,然后我会用语言语法来表达这些概念。

                      for-loop 的概念是一样的。 (Initialize, condition, increment/decrement) 现在,如果您想用 python 编写它,那么您可以编写。例如。

                      start, stop, step = 1, 11, 1
                      for i in range(start, stop, step):
                          print(i, end=', ')
                      # Output: 1 2 3 4 5 6 7 8 9 10 
                      
                      • 开始: 序列的起始编号,default=0. (initialize)
                      • 停止:生成最多但不包括此数字的数字。 (condition)
                      • 步骤: 序列中每个数字之间的差异,default=1. (increment/decrement)

                      其他的答案中已经给出了python for-loop示例。谢谢。

                      【讨论】:

                        猜你喜欢
                        • 2020-12-24
                        • 1970-01-01
                        • 2022-06-10
                        • 1970-01-01
                        • 1970-01-01
                        • 2021-04-25
                        • 2019-05-01
                        • 2021-08-14
                        • 2018-08-12
                        相关资源
                        最近更新 更多