Generators in Python Query - python

I'm trying to understand Generators and have come across the following code:
def reverse(data):
for index in range(len(data)-1, -1, -1):
yield data[index]
for s in reverse([1,2,3]):
print s
I understand that we start at index 2, decrement by 1 and end at -1. What I don't get is the fact that the stopping point -1 ,I thought, should refer to "3", but it appears to refer to "1" here? :
3
2
1
Thanks

Please see https://docs.python.org/2/library/functions.html#range to see how range works. I can see it may be initially confusing to read the documentation but hope my explanation below, for your case, helps.
Specifically these lines from the above doc answers your question:
' The full form returns a list of plain integers [start, start + step, start + 2 * step, ...]. If step is positive, the last element is the largest start + i * step less than stop; if step is negative, the last element is the smallest start + i * step greater than stop'
In your case start=2(len(data)-1), stop =-l and step=-1. So the potential list of integers would be [2, 2-1, 2-2*1, 2-3*1 ...] which is [2,1,0,-1 ...]. However since your step is negative, i.e, -1, the last element would be smallest (start + i*step) greater than stop. In the potential list, the smallest item greater than stop, i.e greater than -1 is 0. So range(len(data)-1, -1, -1) returns [2,1,0]

Related

How do we use minus numbers for step in range

I tried to use -1 for the step in range to reverse the list, in the first code it gave an empty list and in the second, I got what I wanted.
print(list(range(0, 5, -1)))
# Output: []
print(list(range(5, -1, -1)))
# Output: [5, 4, 3, 2, 1, 0]
How do we understand this?
The range generator does a sanity check. If start is lower than end and step is negative then that's impossible - hence the empty list. In your case, you can never get to 5 by decrementing from zero.
In the second case, the range generator will stop generating when end has been reached - i.e. it does not generate the value of end
range(0, 5, -1) -> starting at 0, you cannot reach 5 by successively adding -1. No numbers are encountered along the way.
range(5, -1, -1) -> starting at 5, you can reach -1 by successively adding -1, and it yields the numbers that it'll encounter along the way.
range function is working like this if you add 3 arguments :
range(begin, end, step)
In this case you will begin from begin value, and by implementing the step, reach the (end - abs(step)) value at the end. Remember in python as the end is not reached (which differs from other languages such as Matlab for example).
You must take care of your implementation step according to the value you add as begin and end. It must be consistent, as #Kache was saying.

How to use string slicing to print a string left to right

I have a task uses string slicing. To do this I need to start and the end and go up. up to zero. So if it’s efg It starts at __g goes to _fg and then efg. I’m confused how to do this with string slicing in a while loop.I know to get the last character I would use -1. What I have right now does kind of what I want just not with the last letter and instead the first letter. I know it has to do with my slicing but I can’t figure it out
N=len(s)
I=0
while I< N:
Print((n-I)*” “ + s[0:i:1]
The negative index should be the starting character:
> "efg"[-1:]
"g"
> "efg"[-2:]
"fg"
> "efg"[-3:]
"efg"
I'm not entirely sure what output you were expecting, but this is the code I came up with from what I interpreted:
s = "efg"
n = len(s)
i = 0
while(i < n):
print("_" * (n-i-1) + s[((i+1) * -1):])
i+=1
Output:
__g
_fg
efg
We can print out one less than the difference of i and n number of _'s, and then slice as #chepner mentioned.
The key part of this problem is finding a way to use i and n to get the correct number of repetitions of _ and the correct slicing of s.
Since i will start at 0 and end at 2, and n = 3, and in this example the number of _s goes from 2 to 1 to 0, a simple equation would be: number of _s = n - i - 1.
Similarly, to slice from -1, -2, and then -3 onwards, this relationship is just equal to -(i+1).
I hope this helped! Let me know if you need any further help or clarification.

What a solitaire negative number on range does?

I was doing an exercise from John Zelle's book on Python, he asked to do the Fibonacci sequence using a loop function.
After I didn't manage to get it done, I gave a look at his resolution, which was this:
n = int(input("Enter the value of n: "))
curr, prev = 1, 1
for i in range(n-2):
curr, prev = curr+prev, curr
print("The nth Fibonacci number is", curr)
While I did understand part of what he did, the part that I missed was the (n-2) range.
I gave a look here on Stackoverflow to see about this and people say that a negative number on the range goes back to the end of the list. But in this case, if the user prompts 1, the result will be range(-1).
My guess was that the author did that so that the for loop didn't sum the first two values of the Fibonacci sequence, since they are both 1, and only after the user prompts 2 and forth, the loop actually starts adding up. Am I right on my guess?
If you enter 0 or 1 for this, the code does not enter the loop, and the result is the initial value of curr, that being 1. For any higher value, the loop will iteratively compute the proper value.
Your memory of negative values is a little bit off: a negative index will work from the opposite end of an iterable (e.g. list, tuple, string). A range is not quite in that class; the result in this case is an empty range.
CLARIFICATION after OP comment
I see your confusion. range returns an iterable of the given values. However, it looks like you've confused the limits with the index. Let's work with a general form:
r = range(left, right, step)
r[pos]
left* defaults to 0; **step defaults to 1
Here are some examples:
>>> r = range(0, 20, 2)
>>> r[-1]
18
>>> r = range(0, -1)
>>> r
[]
>>> r = range(0, -10, -2)
>>> r
[0, -2, -4, -6, -8]
>>> r[-2]
-6
Note the second and third examples, where we use negative values for endpoints. There's a distinction between a negative endpoint and a negative index. The endpoint is used to build the list; if the endpoints aren't in the order implied by the step, then the resulting range is the empty list. range(0, -1) is such an example.
Once the list is built, such as with range(0, 20, 2), then a reference into that list with a negative index will count from the right end of the list. Note the third example, making a list that goes "backward", 0 down to -8. A negative index in this case also works from the right. The negative right-end value, the negative step, and the negative index are three distinct usages.
Does that clear up things?
>>> range(-1)
range(0, -1)
So the for loop is not entered if n is 1 or 2 and curr (which is set to 1) is the result.

How to explain the reverse of a sequence by slice notation a[::-1]

From the python.org tutorial
Slice indices have useful defaults; an omitted first index defaults to zero, an omitted second index defaults to the size of the string being sliced.
>>> a = "hello"
>>> print(a[::-1])
olleh
As the tutorial says a[::-1] should equals to a[0:5:-1]
but a[0:5:-1] is empty as follows:
>>> print(len(a[0:5:-1]))
0
The question is not a duplicate of explain-slice-notation. That question is about the general use of slicing in python.
I think the docs are perhaps a little misleading on this, but the optional arguments of slicing if omitted are the same as using None:
>>> a = "hello"
>>> a[::-1]
'olleh'
>>> a[None:None:-1]
'olleh'
You can see that these 2 above slices are identical from the CPython bytecode:
>>> import dis
>>> dis.dis('a[::-1]') # or dis.dis('a[None:None:-1]')
1 0 LOAD_NAME 0 (a)
3 LOAD_CONST 0 (None)
6 LOAD_CONST 0 (None)
9 LOAD_CONST 2 (-1)
12 BUILD_SLICE 3
15 BINARY_SUBSCR
16 RETURN_VALUE
For a negative step, the substituted values for None are len(a) - 1 for the start and -len(a) - 1 for the end:
>>> a[len(a)-1:-len(a)-1:-1]
'olleh'
>>> a[4:-6:-1]
'olleh'
>>> a[-1:-6:-1]
'olleh'
This may help you visualize it:
h e l l o
0 1 2 3 4 5
-6 -5 -4 -3 -2 -1
You are confused with the behavior of the stepping. To get the same result, what you can do is:
a[0:5][::-1]
'olleh'
Indeed, stepping wants to 'circle' around backwards in your case, but you are limiting it's movement by calling a[0:5:-1].
All it does is slice. You pick. start stop and step so basically you're saying it should start at the beginning until the beginning but going backwards (-1).
If you do it with -2 it will skip letters:
>>> a[::-2]
'olh'
When doing [0:5:-1] your'e starting at the first letter and going back directly to 5 and thus it will stop. only if you try [-1::-1] will it correctly be able to go to the beginning by doing steps of negative 1.
Edit to answer comments
As pointed out the documentation says
an omitted second index defaults to the size of the string being
sliced.
Lets assume we have str with len(str) = 5. When you slice the string and omit, leave out, the second number it defaults to the length of the string being sliced, in this case - 5.
i.e str[1:] == str[1:5], str[2:] == str[2:5]. The sentence refers to the length of the original object and not the newly sliced object.
Also, this answer is great
a[0:5:-1] does not make much sense, since when you use this notation the indices mean: a[start:end:step]. When you use a negative step your end value needs to be at an "earlier" position than your start value.
You'll notice that the third slice argument, the step, is not presented in the part of the tutorial you quoted. That particular snippet assumes a positive step.
When you add in the possibility of a negative step, the behavior is actually pretty intuitive. An empty start parameter refers to whichever end of the sequence one would start at to step through the whole sequence in the direction indicated by the step value. In other words it refers to the lowest index (to count up) if you have a positive step, and the highest index (to count down) if you have a negative step. Likewise, an empty end parameter refers to whichever end of the sequence one would end up at after stepping through in the appropriate direction.
The docs simply aren't correct about the default values as you've pointed out. However, they're consistent other than that minor error. You can view the docs I am referring to here: https://docs.python.org/3/library/stdtypes.html#common-sequence-operations
Note that the behavior is definitionaly correct according to the docs:
The slice of s from i to j with step k is defined as the sequence of
items with index x = i + n*k such that 0 <= n < (j-i)/k. In other
words, the indices are i, i+k, i+2*k, i+3*k and so on, stopping when j
is reached (but never including j).
When you do:
>>> a = "hello"
>>> y = a[0:5:-1]
we have that i == 0, j == 5, and k == -1. So we are grabbing items at index x = i + n*k for n starting at 0 and going up to (j-i)/k. However, observe that (j-i)/k == (5-0)/-1 == -5. There are no n such that 0 <= n < -5, so you get the empty string:
>>> y
''
Do a[start:stop][::step] when in doubt (it's almost always what we want)
It's almost always the case that when you pass a negative step to something like x[start:stop:step], what you want to happen is for the sub selection to happen first, and then just go backwards by step (i.e. we usually want x[start:stop][::step].
Futhermore, to add to the confusion, it happens to be the case that
x[start:stop:step] == x[start:stop][::step]
if step > 0. For example:
>>> x = list(range(10))
>>> x
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> x[2:6:2]
[2, 4]
>>> x[2:6][::2]
[2, 4]
>>> x[1:10][::3]
[1, 4, 7]
>>> x[1:10:3]
[1, 4, 7]
Unfortunately, this doesn't hold when step < 0, even though it's tempting to think that it should.
After being burned by this a couple times, I realized it's just safer to always do the step clause after you perform the start:stop slice. So I almost always start with y = x[start:stop][::step], at least when prototyping or creating a new module where correctness/readability is the primiary concern. This is less performant than doing a single slice, but if performance is an issue, then you can do the less readable:
y = x[start:stop:step] if step > 0 else x[stop:start:step]
HTH.
For Python slicing for a sequence[start:stop:step], have derived these rules:
start:stop = start:stop:1
start:stop:(+ or -) step - It means when traversing skip N items in the sequence. However, (-) indicates backward traversal
Remember, position of last item in sequence is -1, and the one before than is -2, and so on..
# start:stop: +step Rules
Always traverse in forward
Always start from beginning of sequence as its a positive step ( forward )
Start at requested position, stop at requested position but exclude the item stop position
Default start: If start is not provided, start at 0
Default stop: if stop is not provided, it means until the end of the sequence including the last value
If item at stop position is not reachable (item is beyond the end of sequence traversal), slice does not return anything
# start:stop:-step Rules
Always traverse in reverse
If start position is provided, start from there, but traverse in reverse ( its a step back )
If stop is provided, stop traversing there but exclude this
Default start: If start position is not provided, start position is the last position of the sequence ( since negative traversal)
Default stop: If stop is not provided, it is the beginning of the list ( position 0)
If item at stop position is not reachable (item is beyond the end of sequence traversal), slice does not return anything

How to understand python slicing with a negative k index?

Can someone explain why a[:5:-1] != a[:5][::-1]?
>>> a = range(10)
>>> a[:5][::-1]
[4, 3, 2, 1, 0]
>>> a[:5:-1]
[9, 8, 7, 6]
The general syntax of slicings is
a[start:stop:step]
You can omit any of the three values start, stop, or step. If you omit step, it always defaults to 1. The default values of start and stop, by contrast, depend on the sign of step: if step is positive, start defaults to 0 and stop to len(a). If step is negative, start defaults to len(a) - 1 and stop to "beginning of the list".
So a[:5:-1] is the same as a[9:5:-1] here,
while a[:5][::-1] is the same as a[0:5][4::-1].
(Note that it's impossible to give the default value for stop explicitly if step is negative. The stop value is non-inclusive, so 0 would be different from "beginning of the list". Using None would be equivalent to giving no value at all.)
What a[:5][::-1] says is that program should firstly take elements until 5th element of the dataset and then reverse them (take every one element starting with the last one).
Contrary to that, a[:5:-1] says that you should take elements until 5th element starting with the last element (take every one element starting with the last one).
a[:5] returns an array, indexes 0 through 4, that you're then negatively indexing as a second operation. a[:5:-1] indexes the original array negatively.

Categories

Resources