Python list slice [0:-1:-1] - python-3.x

I ran into this statement while I was coding:
l = [1,2,3]
print(l[0:-1:-1])
I was expecting this piece of code gives me [1] however it gives me [], makes me think I must have mis-understood python slice operation, can someone explain what is going on here?

In a slice,
The first integer is the index where the slice starts.
The second integer is the index where the slice ends.
The third integer specifies a stride or step causing the resulting slice to skip items. -1 for reverse the output.
l[0:-1:-1]
is equivalent to
l[len(l)-1:len(l)-1:-1]
The first index converted 0 to len(l)-1, because you added -1 in the last index to reverse the list. This will always give you an empty list.

When you use slice in python and you type
l[x:y:-1]
it would somehow be equivalent to
l.reverse()
print(l[y:x])
l.reverse()
but with the difference that -1 reverses the list elements with their indexes
so if you type
l=[1, 2, 3]
l[2:0:-1]
the output will be
[3, 2]
The reason for empty list is that you change the order of indexes so it wont find any element in that window...

I gathered some information after talking with friends about this problem, now we kind of believe this is what happened: when I do l[0:-1:-1], the first thing interpreter will do is to convert that second -1 to the positive index, which is 2 in my case. Then it will iterate backward with the terminating condition being "start <= end", in this case since at the very beginning it will find 0 <= 2, so it will directly terminate, results in the output array being empty.
I didn't really get what the two other answers are saying here (silly me), maybe they are right, I need to look into the python source code for a 100% certain explanation, but for now I believe what I just stated here is the case.

Related

Finding item at index i of an iterable (syntactic sugar)?

I know that maps, range, filters etc. in python3 return iterables, and only calculate value when required. Suppose that there is a map M. I want to print the i^th element of M.
One way would be to iterate till i^th value, and print it:
for _ in range(i):
next(M)
print(next(M))
The above takes O(i) time, where I have to find the i^th value.
Another way is to convert to a list, and print the i^th value:
print(list(M)[i])
This however, takes O(n) time and O(n) space (where n is the size of the list from which the map M is created). However, this suits the so-called "Pythonic way of writing one-liners."
I was wondering if there is a syntactic sugar to minimise writing in the first way? (i.e., if there is a way which takes O(i) time, no extra space, and is more suited to the "Pythonic way of writing".)
You can use islice:
from itertools import islice
i = 3
print(next(islice(iterable), i, i + 1))
This outputs '3'.
It actually doesn't matter what you use as the stop argument, as long as you call next once.
Thanks to #DeepSpace for the reference to the official docs, I found the following:
from more_itertools import nth
print(nth(M, i))
It prints the element at i^th index of the iterable.

Python 3.x indexing and slicing

I have been confused with python indexing and slicing of data structure(lists,etc.). Let me explain the problem. Suppose I have a python list as shown below.
examplelist = ['ram', 'everest' , 'apple', 32, 'cat', 'covid', 'vaccine', 19]
Example one
>> examplelist[-5 : 7 : -1]
>> [ ]
The result is empty set as shown above. Logic, explained in the python tutorial websites, I have checked is the starting count(-5) indicates item 32. End count 7 indicates one item before the stop/end count which is item 'vaccine'. Step size is -1 which means we need to move right to left. But since our starting item is 32 and end item is 'vaccine' there won't be any item if we move leftwards from 32. Hence the result empty list. OK, agreed. Now lets see another example.
Example Two
>> examplelist[::-1]
>> [19, 'vaccine', 'covid', 'cat', 32, 'apple', 'everest', 'ram']
This is quite commonly used to reverse a list in python data structure. If we use the same logic provided for example 1, how can this example have a reversed list. Logically, with starting count 0(meaning start item is 'ram') and end count all the way until the end of the list and with step size -1 means here too we need to move leftwards from starting item i.e. 'ram'. This too has no items in it if we use the same logic. But this example seems to work differently. Why? Is it that reversing a list is an exception to the logic behind python indexing/slicing.
Now lets see another example below.
Example Three
>> examplelist[:-3:-1]
>> [19, 'vaccine']
In this example our starting count is 0 (so we begin at first item i.e. 'ram'), end count is -3 which refers to one item before the end count i.e. item 'cat' and with step size -1 we move leftwards from start to end item. If we follow the logic there is no item to pick if we move leftwards from our starting item to end item. But the answer list is quite different.
My Confusion
I feel that there is no coherent logic working in all examples. Why does the same logic fails to different problem? My understanding it that there is always a standard logical explanation while coding. I tried to figure out some standard logic that will explain all types of indexing/slicing problem with python lists. But with examples listed above, my confusion still persists. Is it that there is a hole in my understanding or there is some standard explanation to this problem which I have not understood yet? Someone please rescue.
Using a negative step reverses default start and end values for a slice.
These are all equal:
examplelist[-5:7:-1]
examplelist[len(examplelist)-5 : 7 : -1]
examplelist[3 : 7 : -1]
And so are all these:
examplelist[::-1]
examplelist[-1::-1]
examplelist[len(examplelist)-1 : -len(examplelist)-1 : -1]
examplelist[7 : -9 : -1]
As well as these:
examplelist[:-3:-1]
examplelist[len(examplelist)-1 : -3 : -1]
examplelist[7 : 5 : -1]
Once you have positive start and end values, it becomes clear what is happening.
In the second example end value must be negative because there is no other way to represent the element before the first element.

What does this syntax mean in Python 3?

I've been seeing this syntax, but I'm not quite sure what it means. It's when two square brackets are next to the name of one list. I'm assuming this does some type of list slicing?
mylist[x][y]
mylist[][]
These are just some examples of what I've seen. (I've used variables x&y to represent an arbitrary number)
This notation can be used when the list contains some other lists as elements, which is helpful to represent the matrices. For example:
a=[[1,2,3],[4,5,6],[7,8,9]]
a[0][0] #This gives the number 1.
In this case, a[0] (the first index) chooses the 1st element, which is [1,2,3]. Then the second index (a[0][0]) chooses the first element of the list defined by a[0], thus giving the answer 1.
The top line just indexes into a list within a list. So for example, you could have
mylist = [
[1,2,3],
[4,5,6],
[7,8,9],
]
value = mylist[1][2]
which will get the value 6.
The bottom line doesn't look like valid Python to me.
EXPLANATION:
Consider that mylist[1] just extracts the second element from mylist (second because of 0-based indexing), which is [4,5,6]. Then adding [2] looks up the third item in that list, which is 6. You could also write
inner_list = mylist[1]
value = inner_list[2]
or
value = (mylist[1]) [2]
which both do the same thing.

TypeError summing a list of integers

I'd like to find the sum of the elements in a list using a loop. I must be able to apply this generically if inputting different lists.
I have tried the simple
print(sum(numbers))
and it returns
TypeError: unsupported operand type(s) for +: 'int' and 'str'.
When I tried adding each individually, I found out that the list changes. The original list is [1, 3, 5, 7, 9]. When I added each element using
int(numbers[0]) + int(number[1]) # ...
when I get to index 4, there isn't a value for the index.
I'm a little unsure by what you mean, because you haven't included much code, but I believe I can answer part of it.
It's hard to know why you're having indexing errors without seeing the code you wrote, but I imagine Because you may have been removing them as you added them up.
Maybe try using an accumulator variable, which would result in python code that might look like
numbers = [1,2,3,4]
total = 0
for i in range(len(numbers)): #loops through for exactly the number of items in the list
sum = total + numbers[i]
This won't change any of the items in the list, leaving intact, and leaving you with the variable sum that is equal to the total sum of the list.

Funny Haskell Behaviour: min function on three numbers, including a negative

I've been playing around with some Haskell functions in GHCi.
I'm getting some really funny behaviour and I'm wondering why it's happening.
I realized that the function min is only supposed to be used with two values. However, when I use three values, in my case
min 1 2 -5
I'm getting
-4
as my result.
Why is that?
You are getting that result because this expression:
min 1 2 -5
parses as if it were parenthesized like this:
(min 1 2) -5
which is the same as this:
1 -5
which is the same as this:
1 - 5
which is of course -4.
In Haskell, function application is the most tightly-binding operation, but it is not greedy. In fact, even a seemingly simple expression like min 1 2 actually results in two separate function calls: the function min is first called with a single value, 1; the return value of that function is a new, anonymous function, which will return the smaller value between 1 and its single argument. That anonymous function is then called with an argument of 2, and of course returns 1. So a more accurate fully-parenthesized version of your code is this:
((min 1) 2) - 5
But I'm not going to break everything down that far; for most purposes, the fact that what looks like a function call with multiple arguments actually turns into a series of multiple single-argument function calls is a hand-wavable implementation detail. It's important to know that if you pass too few arguments to a function, you get back a function that you can call with the rest of the arguments, but most of the time you can ignore the fact that such calls are what's happening under the covers even when you pass the right number of arguments to start with.
So to find the minimum of three values, you need to chain together two calls to min (really four calls per the logic above, but again, hand-waving that):
min (min 1 2) (-5)
The parentheses around -5 are required to ensure that the - is interpreted as prefix negation instead of infix subtraction; without them, you have the same problem as your original code, only this time you would be asking Haskell to subtract a number from a function and would get a type error.
More generally, you could let Haskell do the chaining for you by applying a fold to a list, which can then contain as many numbers as you like:
foldl1 min [1, 2, -5]
(Note that in the literal list syntax, the comma and square bracket delimit the -5, making it clearly not a subtraction operation, so you don't need the parentheses here.)
The call foldl1 fun list means "take the first two items of list and call fun on them. Then take the result of that call and the next item of list, and call fun on those two values. Then take the result of that call and the next item of the list..." And so on, continuing until there's no more list, at which point the value of the last call to fun is returned to the original caller.
For the specific case of min, there is a folded version already defined for you: minimum. So you could also write the above this way:
minimum [1, 2, -5]
That behaves exactly like my foldl1 solution; in particular, both will throw an error if handed an empty list, while if handed a single-element list, they will return that element unchanged without ever calling min.
Thanks to JohnL for reminding me of the existence of minimum.
When you type min 1 2 -5, Haskell doesn't group it as min 1 2 (-5), as you seem to think. It instead interprets it as (min 1 2) - 5, that is, it does subtraction rather than negation. The minimum of 1 and 2 is 1, obviously, and subtracting 5 from that will (perfectly correctly) give you -4.
Generally, in Haskell, you should surround negative numbers with parentheses so that this kind of stuff doesn't happen unexpectedly.
Nothing to add to the previous answers. But you are probably looking for this function.
import Data.List
minimum [1, 2, -4]

Resources