Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Episode 2: range doesn't return an array #491

Open
biavillasboas opened this issue Mar 21, 2018 · 11 comments
Open

Episode 2: range doesn't return an array #491

biavillasboas opened this issue Mar 21, 2018 · 11 comments

Comments

@biavillasboas
Copy link

@biavillasboas biavillasboas commented Mar 21, 2018

In episode 2 (Repeating Actions with Loops) the first exercise is called "From 1 to N". This is a good exercise, but I believe its description might give rise to some confusion for novices.

The exercise introduces the concept of looping through the elements of a collection using Python's built-in function "range". On the first bullet point explaining what range does, we have:

  • If one parameter is given, range creates an array of that length ...

However, the function range in Python2 returns a list and in Python3 returns an object of range type. Since learners had just been introduced to lists and arrays and how to find out type(some_thing), it would be important to be consistent and specific about types.

Maybe to avoid going into the details of the type range this early in the lesson, we could simply use numpy.arange (which would return an array) throughout this exercise without compromising its overall goal.

@labrax
Copy link

@labrax labrax commented Mar 21, 2018

To avoid these problems we can write this point similar to the next ones:

Instead of:

If one parameter is given, range creates an array of that length, starting at zero and incrementing by 1. For example, range(3) produces the numbers 0, 1, 2.

We can write as:

If one parameter is given, range starts at 0 and ends just before the parameter given, incrementing by one. For example, range(3) produces the numbers 0, 1, 2.

Using this approach we omit the explanation of the function return and explain what is the goal of the lesson.

@biavillasboas biavillasboas changed the title Episode 2 - range doesn't return an array [instructor checkout] Episode 2 - range doesn't return an array Mar 21, 2018
@biavillasboas
Copy link
Author

@biavillasboas biavillasboas commented Mar 21, 2018

Thanks for the suggestion. I agree this would be the simplest solution to the problem.

However, I'm still a little concerned about the fact that the built-in range function in python3 returns an object of type range. I imagine that if a learner tries to understand what is going on the exercise and type

print(range(3))

the learner will not get 0, 1, 2, which is what you would expect from the explanation in the exercise:

If one parameter is given, range starts at 0 and ends just before the parameter given, incrementing by one. For example, range(3) produces the numbers 0, 1, 2.

I believe this could be rather confusing.

@labrax
Copy link

@labrax labrax commented Mar 26, 2018

Indeed, my solution will not help in the case of Python 3. Thank you.

How about we create a case just like the ones before, something in the lines of:

    Python has a built-in function called range that assists creating a sequence of numbers. range can accept 1, 2, or 3 parameters.

    If one parameter is given, _range_ will return to _for_ an array of that length, starting at zero and incrementing by 1. For example:

    for number in range(3):
        print(number)

    0
    1
    2

    If two parameters are given, our _for_ starts at the first and ends just before the second, incrementing by one. For example:

    for number in range(2,5):
        print(number)

    2
    3
    4


    If _range_ is given 3 parameters, our _for_ starts at the first one, ends just before the second one, and increments by the third one. For example:

    for number in range(3, 10, 2):
        print(number)

    3
    5
    7
    9
@annefou
Copy link
Contributor

@annefou annefou commented Mar 27, 2018

I don't think we should bother about python 2 as our lesson is python 3. But you are right, the current lesson is a bit confusing because it does not really explain what the built-in range does.
For instance, it is important to undersand why
print(range(3))
returns
range(0, 3)
and not 0,1,2
It has important consequences on the memory usage. You can iterate over a huge number of values and range(n) (where n is a large number) will take as little memory as range(1).

@semodi
Copy link

@semodi semodi commented Mar 27, 2018

Instead of introducing the concept of the built-in range function, would it be enough to say that range() does only make sense in the context of a for-loop and should not be used by itself ?
Also, I like @labrax's way of explaining it as 'instructions' for the for loop where to start and where to stop. Even though this might not be precisely what happens in the background, I think it's a good low-level explanation of how an iterator works.
Anyway the expression 'array' should definitely be avoided as it is simply incorrect.

@maxim-belkin
Copy link
Contributor

@maxim-belkin maxim-belkin commented Mar 27, 2018

hmmm, how about list(range(4)) ?

@maxim-belkin maxim-belkin changed the title [instructor checkout] Episode 2 - range doesn't return an array Episode 2: range doesn't return an array Mar 28, 2018
@maxim-belkin
Copy link
Contributor

@maxim-belkin maxim-belkin commented Mar 28, 2018

SWC Python lesson has to have range in it. The fact that range does not return a list could be explained as follows:

Let's say, we need to iterate over a loop 1,000,000 times. Sure we can iterate over a list with 1,000,000 elements... but every step we will be using only one element, so we don't really need to have all elements of a list at once. That is why Python 3+ does not create such a list when we call range and why type(range(5)) returns range.

@iglpdc
Copy link
Contributor

@iglpdc iglpdc commented May 5, 2018

I agree that the bullet points should be reworded and I think @labrax 's wording is pretty accurate (for the stage we are at in the lesson):

If one parameter is given, range starts at 0 and ends just before the parameter given, incrementing by one. For example, range(3) produces the numbers 0, 1, 2.

Apart from being iterable, range supports the interface you would expect on an array-like thing:

>>> a = range(1,4)  # array-like 1, 2, 3
>>> len(a)
3
>>> a[0]
1
>>> a[1]
2
>>> a[-1]
3
>>> b = a[::2]  # pick every other element, so 1, 3
>>> b[0]
1
>>> b[1]
3

To me the fact that range returns a weird type of object or is "lazy" is just an implementation detail, which should not be a concern at this point.

@maxim-belkin
Copy link
Contributor

@maxim-belkin maxim-belkin commented May 6, 2018

To me the fact that range returns a weird type of object or is "lazy" is just an implementation detail, which should not be a concern at this point.

100% agree.

@gkanarek
Copy link

@gkanarek gkanarek commented Jun 13, 2018

It already says that "range creates a sequence of numbers". Why introduce extra (misleading) terminology? That bullet point could be:

If one parameter is given, range creates a sequence of that length, ...
appeltel added a commit to appeltel/python-novice-inflammation that referenced this issue Mar 27, 2019
Use the phrase "generates a sequence" to use consistent
language for lazy-evaluators as iterables in a for loop
and avoid suggesting that the sequence is stored as an
array in memory. See issue swcarpentry#491.
@appeltel
Copy link
Contributor

@appeltel appeltel commented Mar 27, 2019

I agree that using the word "array" here is suggesting an incorrect mental model of the range object as actually storing values in memory.

I also agree that range should be taught as a basic Python concept. People coming from a background in C-family languages are naturally going to want an equivalent to for ( int i = 0; i < 10; i++) { ... } and its really common to need to run over a simple sequence of integers. It is silly to actually store a big array of sequential integers in memory, and range lets you avoid that with a simple and fast implementation. While this memory usage isn't an initial concern for the beginning programmer here, it is more than an implementation detail as it is guaranteed in the language reference:

The advantage of the range type over a regular list or tuple is that a range object will always take the same (small) amount of memory, no matter the size of the range it represents (as it only stores the start, stop and step values, calculating individual items and subranges as needed).

A range object is a Sequence type which is a formally defined python term of something that interfaces essentially like an array and follows a specific interface. For example, I can access individual elements of a range by subscript:

>>> numbers = range(0, 100, 5)
>>> numbers[6]
30

So the best language that I can come up with here is "generates a sequence". These terms make sense informally so that the learner gets the idea without being burdened with the implementation of the object or the formalities of the python language reference. They also align with more formal Python language that might be understood later. A range object is formally a Sequence in Python, and the term "generate" will call to mind "generator functions" or "generator expressions". While a range object is not formally a generator object, these Python concepts all relate to the idea of lazy evaluation.

Furthermore, the phrase "generates a sequence" is also used in the later enumerate example, so there would be consistent language applied to lazy evaluators mentioned in this lesson. Note that formally an enumerate object is not a Sequence as you can't call len() on it or access by subscript, but I think that it's probably overly pedantic to insist on changing that given how enumerate is commonly used.

maxim-belkin pushed a commit that referenced this issue Oct 23, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
9 participants