if statements, comparing lists, python - if-statement

what am i doing wrong with my if statement that it doesn't recognise if an element in a is equal to 0? what i am attempting to print is for ever 0 the program prints . and for ever 1 #. cheers.
a=[0,0,1,0,1,1,0,1,1,0,0,0,0,1]
print(a)
for i in range(len(a)):
if a[i]==[0]:
print('.', end='')
else:
print('#', end='')
print()
bash:
[0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1]
##############

You probably want
if a[i] == 0:
instead of
if a[i] == [0]:
You want to compare the items to the integer value 0, not to the single-element list [0].

Related

Python List - wierd behaviour

I was trying to solve the 3Sum problem in leetcode. but I observed python lists behaving different during the end of loop statement.
def threeSum(nums):
n=len(nums)
sum = {}
result = []
for i in range(n):
for j in range(i+1,n):
if i != j:
key = nums[i]+nums[j]
if key not in sum:
sum[key] = [nums[i],nums[j]]
for i in range(n):
if -nums[i] in sum:
temp = sum[-nums[i]]
temp.append(nums[i])
if(len(temp)<=3):
result.append(temp)
print(result)
print("at the end of loop")
print(result)
return "result printed"
nums = [-1,0,1,2,-1,-4]
print(threeSum(nums))
For the above function I got the output as
[[-1, 2, -1]]
[[-1, 2, -1], [-1, 1, 0]]
[[-1, 2, -1], [-1, 1, 0], [-1, 0, 1]]
[[-1, 2, -1], [-1, 1, 0], [-1, 0, 1], [-1, -1, 2]]
at the end of loop
[[-1, 2, -1, -1], [-1, 1, 0], [-1, 0, 1], [-1, -1, 2]]
result printed
From the output you can see that during the last iteration of the loop the result List variable contains the value [[-1, 2, -1], [-1, 1, 0], [-1, 0, 1], [-1, -1, 2]] but when I print the same result at the end of the loop it is printed as [[-1, 2, -1, -1], [-1, 1, 0], [-1, 0, 1], [-1, -1, 2]] , the first element in List is changed.
How do you explain this? Am I missing something in the understanding of Python Lists?
P.S : Please ignore the solution of 3Sum problem, I already found another way to solve it, my question is regarding the Python List only
In Python, List is reference value. In your code, you refer to sum[1] 2 times. Both of 2 times return to the same List instance. That's why after the 2nd time, that List instance is appended 1 more number
This behavior is caused by two issues:
if(len(temp)<=3): will prevent printing the final result due to the length constraint
python lists are mutable and they can be modified from different places if the same object is referenced
In your case, at fourth iteration result[0] and temp will reference the same object. This is why result gets modified even it was not apparently touched. It was changed due to the change of temp variable. You can check this using additional prints to highlight current iteration, result and object ids.
for i in range(n):
print(i)
print(result)
if -nums[i] in sum:
temp = sum[-nums[i]]
temp.append(nums[i])
if(len(temp)<=3):
result.append(temp)
print(result)
print(id(result[0]))
print(id(temp))
print("at the end of loop")
print(result)
return "result printed"

Python: How to make values of a list in a list of lists zeros

I would like to make all the values in the first list inside the list of lists named "child_Before" below zero. The piece of code I wrote to accomplish this task is also shown below after the list:
child_Before = [[9, 12, 7, 3, 13, 14, 10, 5, 4, 11, 8, 6, 2],
[1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1],
[[1, 0], [1, 1]]]
for elem in range(len(child_Before[0])):
child_Before[0][elem] = 0
Below is the expected result:
child_After = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1],
[[1, 0], [1, 1]]]
However, I think there should be a more nibble way to accomplish this exercise. Hence, I welcome your help. Thank you in advance.
just to add a creative answer
import numpy as np
child_Before[0] = (np.array(child_Before[0])&0).tolist()
this is bad practice though since i'm using bitwise operasions in a senario where it is not intuitive, and i think there is a slight chance i'm making 2 loops xD on the bright site the & which is making all the zeros is O(1) time complexity
Just create a list of [0] with the same length as the original list.
# Answer to this question - make first list in the list to be all 0
child_Before[0] = [0] * len(child_Before[0])
As for you answer, I can correct it to make all the elements in the lists of this list to be zero.
# Make all elements 0
for child in range(len(child_Before)):
child_Before[child] = [0] * len(child_Before[child])
Use list comprehension:
child_after = [[i if n != 0 else 0 for i in j] for n, j in enumerate(child_Before)]

Using index reference in a for loop iterations [duplicate]

Here is a snippet of code which gives the output: 0 1 2 2. I had expected the output 3 3 3 3 since a[-1] accesses the number 3 in the list. The explanation given online says "The value of a[-1] changes in each iteration" but I don't quite understand how or why. Any explanations would be great!
a = [0, 1, 2, 3]
for a[-1] in a:
print(a[-1])
While doing for a[-1] in a, you actually iterate through the list and temporary store the value of the current element into a[-1].
You can see the loop like these instructions:
a[-1] = a[0] # a = [0, 1, 2, 0]
print(a[-1]) # 0
a[-1] = a[1] # a = [0, 1, 2, 1]
print(a[-1]) # 1
a[-1] = a[2] # a = [0, 1, 2, 2]
print(a[-1]) # 2
a[-1] = a[3] # a = [0, 1, 2, 2]
print(a[-1]) # 2
So, when you are on the third element, then 2 is stored to a[-1] (which value is 1, but was 0 before and 3 on start).
Finally, when it comes to the last element (and the end of the iteration), the last value stored into a[-1] is 2 which explains why it is printed twice.
What's happening here is a list is mutated during looping.
Let's consider following code snippet:
a = [0, 1, 2, 3]
for a[-1] in a:
print a
Output is:
[0, 1, 2, 0]
[0, 1, 2, 1]
[0, 1, 2, 2]
[0, 1, 2, 2]
Each iteration:
reads value from position currently pointed by internal pointer
immediately assigns it to last element in list
after that last element is printed on standard output
So it goes like:
internal pointer points to first element, it's 0, and last element is overwritten with that value; list is [0, 1, 2, 0]; printed value is 0
internal pointer points to second element, it's 1, and last element is overwritten with that value; list is [0, 1, 2, 1]; printed value is 1
(...)
at last step, internal pointer points to last element; last element is overwritten by itself - list does not change on last iteration; printed element also does not change.
This code
a = [0, 1, 2, 3]
for a[-1] in a:
print(a[-1])
is equivalent to
a = [0, 1, 2, 3]
for i in range( len(a) ):
a[-1] = a[i] # update last element of list(updated) with element at i'th position
print(a[-1]) # print last element of list
the output will be 0 1 2 2
Explanation:
len(a) = 4, range( len(a) ) = [0, 1, 2, 3]
1st loop, current list = [0, 1, 2, 3], i = 0 => a[i] = a[0] = 0, updated list = [0, 1, 2, 0], last element a[-1] = 0
2nd loop, current list = [0, 1, 2, 0], i = 1 => a[i] = a[1] = 1, updated list = [0, 1, 2, 1], last element a[-1] = 1
3rd loop, current list = [0, 1, 2, 1], i = 2 => a[i] = a[2] = 2, updated list = [0, 1, 2, 2], last element a[-1] = 2
4th loop, current list = [0, 1, 2, 2], i = 3 => a[i] = a[3] = 2, updated list = [0, 1, 2, 2], last element a[-1] = 2

Strange behavior modifying a list while looping over it (Python)

I understand that modifying a list while iterating over it can spell disaster. I was curious so I tried it anyway. In the first few examples below, things go as expected; but then something unusual happens in the second to last example.
>>> A = [0, 0, 0, 0]
>>> for k in A:
if k == 0:
A.remove(k)
>>> A
[0, 0]
>>> A = [0, 0, 0, 0, 1]
>>> for k in A:
if k == 0:
A.remove(k)
>>> A
[0, 0, 1]
>>> A = [0, 0, 0, 0, 1, 1]
>>> for k in A:
if k == 0:
A.remove(k)
>>> A
[0, 0, 1, 1]
>>> A = [0, 0, 0, 0, 1, 1, 0] # Why does the presence of a fifth zero (the one at the end), cause an earlier zero to be removed?
>>> for k in A:
if k == 0:
A.remove(k)
>>> A
[0, 1, 1, 0]
>>> A = [0, 0, 0, 0, 1, 1, 2]
>>> for k in A:
if k == 0:
A.remove(k)
>>> A
[0, 0, 1, 1, 2]
I am not a Python expert, but just when I imagine how foreach loop is implemented:
len = size(array)
for i in range(0, len):
loop_body(array[i])
Then for the second example:
len = 5, array=[0, 0, 0, 0, 1]
First iteration: i=0, array=[0, 0, 0, 0, 1], zero in array[0] is removed.
Second iteration: i=1, array=[0, 0, 0, 1], zero in array[1] is removed.
Third iteration: i=2, array=[0, 0, 1], array[2] == 1, nothing happened.
And this is your result. The same for the last one.
The remove method removes the first occurrence of x (I knew that, but forgot about it completely!) So when I execute this code:
>>> A = [0, 0, 0, 0, 1, 1, 0]
>>> for k in A:
if k == 0:
A.remove(k)
the presence of the zero at the end causes the zero in front of it (the one that was skipped over while iterating) to be removed. This produces:
>>> A
[0, 1, 1, 0]
I expected:
>>> A
[0, 0, 1, 1, 0]

tiered while loops with multiple outputs

I am trying to learn about arrays. I know that python has lists, not arrays, but the idea is the same. I have a list of lists setup like an array, and I am trying to modify them for random art fun, but I can only get one resulting random number out of this piece of code.
##prior list creation code making a large list of zeros called "array"###
while 3 not in array:
i= random.randint(1,9)
j= random.randint(1,28)
if (i%3)!=0 and (j%7)!=0:
if 5 in array:
array[i][j]=3
return array
elif 3 in array:
array[i][j]=4
return array
else:
array[i][j]=5
amy=(i, j)
return array
continue
##the resulting list called "array" does not chnange any zero to any number except one to "5"##
I have cut the code that made an array filled with zeros. The only number that will show up is the 5... ideally, I would have each number only show up once with each run, but in different spots
What am I doing wrong? I don't fully understand arrays, so that might be it, but I'm having trouble searching what I think the problem might be. Any help you can provide would be great!
Edit:
Sorry about forgetting, the array is the proper size to hold the data (9 rows by 28columns), and it isn't throwing any errors or exceptions... that should have been in there before I posted.
It's hard to tell exactly what you are asking, but I think you just wanted to randomly assign the numbers 3, 4, and 5 somewhere inside your matrix with the condition that i is not divisible by 3 and j is not divisible by 7. If that's what you want, then this should do it:
import random
array = [ [ 0 for j in range(28) ] for i in range(9) ]
for n in [3, 4, 5]:
while True:
i = random.randint(1, 9)
if i % 3 != 0:
break
while True:
j = random.randint(1, 28)
if j % 7 != 0:
break
array[i][j] = n
print('\n'.join(str(a) for a in array))
So let's talk about how to get to this answer. First off, we want a way to generate random numbers until a condition happens. In most languages, this would involve a do-while loop, but Python doesn't have those. However, we can make something that is equivalent to a do-while loop using just a while loop:
# This says to run this loop *forever*
while True:
# do something here
pass # This means "do nothing" in Python
if condition:
# This says if the previously mentioned condition
# is True then we will stop executing the
# currently containing loop.
break
So this construct, that I just showed is a building block which you can use to make a loop that runs until a condition is met.
Let's see how that fits in your original example. We want a random number in the range [1, 9) that is not divisible by 3. The random.randint function will provide a random number in that range, but it doesn't guarantee that it is not divisible by 3. So we need to enforce that constraint ourselves. One way to accomplish that is to simply generate a new number if the constraint is not met.
So now we can use the previously discussed loop construct to build a loop that runs until we have a number that is not divisible by 3:
while True:
i = random.randint(1, 9)
if i % 3 != 0:
break
I'm sure you can do the logical replacement in the previous loop to see how it fits the other example.
So now we can talk a little bit more about where you went wrong in your original code, and how you can prevent from making those same mistakes in the future.
Let's talk about this line first:
while 3 not in array:
First of all, when developing code, it's a very good idea to try things out at the Python interactive interpreter prompt. This is especially true when you are trying out a feature of the language for the first time. As I showed in my comment, the condition in your while loop is always False. The reason for that is clear if you try it out in the interpreter:
>>> 3 in [[3],[3],[3]]
False
The in operator only looks one level deep into a list. Also, it's always a good idea to start small when testing things out interactively. Notice I'm using a list of only 3 elements with nested lists containing only 1 element each instead of your original example of a 9 element list with nested lists containing 28 elements.
Now, another approach that we could have taken to make your loop condition change over time would be to make a "recursive" version of the in operator. Alternatively, we could have just hard coded it to expect a list that contains lists. I'm going to take this second approach because it is simpler, and I don't know if you are already familiar with recursion, and this is already a long explanation.
def contains2d(outer, element):
"""Expects a 2D-array-like list. Returns True if any of the inner
sub-lists within the outer list contain element.
"""
return any([e in inner for inner in outer])
If we try this function out, we'll see that it behaves as you originally expected the in operator to behave:
>>> contains2d([[3],[3],[3]], 3)
True
>>> contains2d([[0],[0],[3]], 3)
True
>>> contains2d([[0],[0],[0]], 3)
False
So now let's talk about your misunderstanding of the continue keyword. A while loop repeats automatically. You don't need to use continue inside of a while loop for it to repeat. The continue keyword is only used to skip the rest of the body of a loop. This is generally used if you have some special case for which you don't want to do the normal loop processing. Here's a reasonable example of how you might use continue:
>>> for i in range(10):
... if i % 3 == 0:
... continue
... print(i)
...
1
2
4
5
7
8
Notice that it really doesn't make any sense to put continue at the end of a loop:
while True:
print("This loop runs forever!")
# The following continue is useless
continue
Alright, so now that we have contains2d, we can start to think about what we want. In your example, you say you want the array variable to contain 3,4, and 5 at the end of your loop. Again, let's start small and see if the condition is True under the desired circumstances. We know from earlier that contains2d([[0],[0],[3]], 3) == True, so that is an insufficient loop termination criteria. Remember, we want the loop to only be true when all conditions have been met. So that means we need to use the and operator
>>> contains2d([[0],[0],[3]], 3) and contains2d([[0],[0],[3]], 4) and contains2d([[0],[0],[3]], 5)
False
>>> contains2d([[4],[0],[3]], 3) and contains2d([[4],[0],[3]], 4) and contains2d([[4],[0],[3]], 5)
False
>>> contains2d([[4],[5],[3]], 3) and contains2d([[4],[5],[3]], 4) and contains2d([[4],[5],[3]], 5)
True
Note that this condition is very ugly and hard to write. Ideally, we'd probably like to refactor it. One way to do that is to use the built-in all function. I'll let you experiment with it on your own, but here's the end result:
>>> all([contains2d([[4],[5],[3]], e) for e in [3,4,5]])
True
That's much shorter and much more clear. So, moving on, we want to run this loop as long as that condition is not True:
while not all([contains2d(array, e) for e in [3,4,5]]):
# ...
The next two lines are actually just fine, but I would format them according to PEP-8:
while not all([contains2d(array, e) for e in [3,4,5]]):
i = random.randint(1, 9)
j = random.randint(1, 28)
# ...
If we replace the in conditions with our contains2d function, then we're almost getting to a working solution:
while not all([contains2d(array, e) for e in [3,4,5]]):
i = random.randint(1, 9)
j = random.randint(1, 28)
if i % 3 != 0 and j % 7 != 0:
if contains2d(array, 5):
# ...
elif contains2d(array, 3):
# ...
else:
# ...
The assignments within the if conditions are perfectly fine as well. However, when you have a return inside of a loop, that will exit the loop and exit the entire containing function. In this code you don't actually have a containing function, so that will just end your program. That's not what you want to do here, so let's just drop all of those return statements:
while not all([contains2d(array, e) for e in [3,4,5]]):
i = random.randint(1, 9)
j = random.randint(1, 28)
if i % 3 != 0 and j % 7 != 0:
if contains2d(array, 5):
array[i][j] = 3
elif contains2d(array, 3):
array[i][j] = 4
else:
array[i][j] = 5
This program is very close to being right. However, the innermost if block is using some odd logic. If you run this progam using my initialization and output, you'll see something like this:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4]
[0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 5, 4]
[0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4]
[0, 4, 4, 4, 4, 4, 4, 0, 3, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4]
It actually took me a minute to figure out what was going wrong, but the key obsevation is that the first time through the loop for which you get a successful pair of i and j, you'll take the else branch randomly placing a 5 in the nested list. The next successful time you get inside the if, you'll take the first branch which randomly places a 3 inside the nested list. However, here's where things go sideways. On the next succssful iteration, you'll still take the first branch, so you'll randomly place another 3 inside the nested list. You will keep doing this for several more iterations. Eventually, you will overwrite the previously written 5. At that point, the first condition will no longer hold and instead you will start taking the second branch which randomly places a 4 in the nested list. This branch will continue to be taken since 5 is no longer present in the nested list. Eventually, you will overwrite all of the previously written 3s with 4s in the nested list. At that point, the second branch condition will no longer hold, and you will land on the else branch again, writing a 5 to the nested list. Finally, the first condition will hold again, so on the next successful iteration, you'll write a 3 again, and as long as you don't get unlucky, that will miss the 5 and you'll now have met the termination criteria for the outer while loop leaving you with a bunch of 4s and just a single 3 and a single 5.
Maybe this is what you wanted to do, but it didn't seem that way from your question, so let's assume you really just wanted one of each of the numbers present in the nested list. If that's the case, we can easily correct the previous program. We just need to fix the conditions for each of the if cases. We only want to take the first branch when a 5 is present, but a 3 is not present. Thus that gives us if contains2d(array, 5) and not contains2d(array, 3):. Furthermore, we only want to take the second branch if 5 and 3 are both already present. Thus that gives us elif contains2d(array, 5) and contains2d(array, 3):. Finally, we only want to take the last branch if a 5 is not present. Thus we must change the else to another elif giving us elif not contains2d(array, 5):. Putting this all together gives us:
array = [ [ 0 for j in range(28) ] for i in range(9) ]
while not all([contains2d(array, e) for e in [3,4,5]]):
i = random.randint(1, 9)
j = random.randint(1, 28)
if i % 3 != 0 and j % 7 != 0:
if contains2d(array, 5) and not contains2d(array, 3):
array[i][j] = 3
elif contains2d(array, 5) and contains2d(array, 3):
array[i][j] = 4
elif not contains2d(array, 5):
array[i][j] = 5
print('\n'.join(str(a) for a in array))
This actually works like my original answer. However, it's not very satisfactory because the logic inside the if block is quite complicated. There's actually very rigid sequence of events that must happen. Whenever you think of a sequence of things, you should think of a list. In this case, the sequence goes like this, we assign 5, then we assign 3, then finally we assign 4. That can be represented as this list: [5, 3, 4]. If we reorder things a bit, we can get the following program:
array = [ [ 0 for j in range(28) ] for i in range(9) ]
for n in [5, 3, 4]:
while not contains2d(array, n):
i = random.randint(1, 9)
j = random.randint(1, 28)
if i % 3 != 0 and j % 7 != 0:
array[i][j] = n
print('\n'.join(str(a) for a in array))
This particular program does have a flaw in that it's possible for one of the later values to overwrite one of the earlier values, and thus the postcondition of all the numbers being present could possibly not hold if your random numbers happen to collide. In fact, my initial answer has this same issue. I'll leave fixing that as an exercise for you to figure out.