python, loop nesting, functions, if statements - python

This is a somewhat garbled code excerpt.
The context is that it is trying to iterate through a list, building, which should look like [100, 92, 87, etc]. It wants to iterate through each floor of the building and then move that person (by decrementing the current floor) to the next available staircase.
My issue is nesting list and if statements within functions. It's a triple whammy and I'm having trouble understanding the proper syntax for:
calling on specific parts of lists of lists - i.e. list[i][j]
using if statements in loops
nesting loops
using loop variables in other loops
Here is my code:
def Evacuate(building):
while sum(building) > 0:
for i in building:
if building[i] > 0:
for j in range(STAIRCASE):
if staircasenetwork[j[i-1]] < CAPACITY:
building[i] -= 1
staircase[i-1] += [TRAVELTIME]
EDIT:
I figured out the issue. Building's input is a list. Staircasenetwork is also a list. The lists look like this
building = [100,90,101]
staircasenetwork = [[0,0,0],[0,0,0]]
This represents a 3 story building full of people and two empty staircases.
What I did wrong was I was trying to write something along the lines of:
for i in building:
#I'm skipping or simplifying some specific conditionals for the problem here
for j in staircasenetwork:
building[i] -= 1
staircasenetwork[j][i] += 1
Which was supposed to take (i) in building (which I expected to be [0, 1, 2]) and (j) in staircasenetwork (which I expected to be [0, 1]) and use those two co-ordinates to iterate through the lists I wrote.
What I forgot was that python iterates through a list by directly assigning those values to (i).
So if I have a list:
[100, 90, 101]
And I write:
for i in list:
print i
It will print:
100
90
101
Not:
0
1
2
So the fix to my problem was using range(len(building) instead of building.
This statement takes building and first converts it into an integer equal to the length of the list building using len(). Then it takes that integer and converts it into a list of numbers from 0 to X, using range().
Essentially: [100, 90, 101] >> 3 >> [0, 1, 2]
building = [100, 90, 101]
for i in range(len(building)):
print str(range(len(building))) + " " str(building)
Will print:
0 100
1 90
2 101
So in my first attempt, when i use:
for i in building:
#to call on
building[i]
The first thing it does is it looks at the first value in the building list, which is 100. Then it uses that number to find the 100th number in the building list, which doesn't exist because that list is only 3 values long.
It tries to do this:
building = [100, 90, 101]
for i in building
>>> first val of building = 100
building[100]
>>> IndexError: list index out of range
>>> Is looking for the 100th number in a 3-number long list
There are ways of using statements like:
for i, e in enumerate(building):
To create a list of tuples that may look like:
[(0, 100), (1, 90), (2, 101)]
But I preferred to use the range(len(building)) method. This method may not meet great benchmarks for speed, but it succeeds in teaching me something about how code works and it solves my little problem.
The fixed code will read:
for i in range(len(building)):
for j in range(len(staircasenetwork)):
building[i] -= 1
staircasenetwork[j][i] += 1
Now (i) and (j) will be the co-ordinates of (via range(len())) rather than direct reflections of the variables.
Lego Stormtrooper has also written a great response to other issues with my code.

Calling on specific parts of lists of lists - i.e. list[i][j]
You've got this correct in you question, but not in your code. Consider:
x = [ 1 , [2,3] ]
When we call x[0] we get an object, in this case the integer 1. Not much else to do.
But when we call x[1] we get a list ([2,3]) and then we can operate on that, like so x[1][1] which would give us 3.
In your code, break it down like this:
staircasenetwork[j[i-1]]
Is equivilent to:
x = j[i-1]
staircasenetwork[x]
So unless x is a valid index, it will fail.
Using if statements in loops
Use these like you would anywhere else:
for i in range(10):
if i%2 == 1:
print i
Will only print odd numbers below 10. Not much to it.
Nesting loops
Again, these operate like anywhere else. But you aren't accessing your items correct. In Python for x in some_list iterates through the items in the list, not the indexes.
So you want something like:
for floor in building:
if floor > 0:
Using loop variables in other loops
Variables have scope in their block, so:
for i in range(3):
x = i*2
# i and x in scope
for j in range(x):
# i, j, x and y are all in scope
y = i+j+x
print y
# The next line will fail because ONLY y isn't in scope.
# j is in scope, but is set to the final value of j for the above loop.
print i+j+x+y
Lastly, miscellaneous issue.
sum isn't magic. It won't do what you think there, as sum takes an iterable (list) of integers. You are passing an iterable of iterables.
Some of your variables are unset - specifically STAIRCASE, CAPACITY and TRAVELTIME

Related

Doing math with numbers in a list

i want to be able to add, subtract, divide, multiply etc with integers in a list and in order.
I know you can use sum() to add, but i also want to be able to subtract, etc in order... so i tried making a for loop idk if thats the right thing to do, but it doesn't give me the right output and it really confuses me because it really seems like it should work. I was wondering if anyone knows how to fix this or explain why its not giving me the same output as i expected.
my_list = [100, 15, 3]
for i in my_list:
i -= i
print(i)
# 100 - 15 - 3 = 82
# Wanted output: 82
# Actual output: 0
my_list = [100, 15]
for i in my_list:
i += i
print(i)
# 100 + 15 = 115
# Wanted output: 115
# Actual output: 30
There are two main issues with your code:
i can't be your loop variable and the sum, because it will be overwritten all the time. So make two variables.
Your first task is different from the second. The sum is easy: take all the values of the list and add them, so the order is irrelevant. For your subtraction it's different because you have to take the first value and subtract all others, so it's basically +100-15-3, which means that also the order of the values in the list matter.
There are more elegant ways to solve it, but for the beginning this should be better to understand.
my_list = [100, 15, 3]
my_difference = my_list[0] #initialize with the first value of your list
my_list_sub = my_list[1:] #make a new list with the remaining numbers
for val in my_list_sub:
my_difference=my_difference-val
print(my_difference)
my_list = [100, 15]
my_sum = 0 #initialize your sum with 0
for val in my_list:
my_sum=my_sum+val
print(my_sum)
As others already pointed out: The "running"/temporary variable is overwritten in every loop. You can try this out with a simple test:
for entry in [0, 'a', 13.37]:
print(entry)
It's always a good idea of trying out what happens in simple cases to learn what is going on.
But your idea of solving this with a loop is absolutely fine. If you want to re-use this functionallity later, it is also nice to wrap that in a function.
Assume integer values my_values = [100, 50, 123, 51, 124, 121] in the following examples.
Lets first tacle the sum.
def calculate_sum(values: list) -> int:
result = 0
for entry in values:
result += entry
return result
Check that it does what we want with
print(calculate_sum(my_values))
print(sum(my_values))
Now difference is 'almost' like summing up, but you want to sum up all values but the first one, and then compute the difference to the first one (a-b-c-d = a-(b+c+d)). Great, that we have already a method for summing up stuff, so we could simply do
def calculate_difference(values: list) -> int:
first, *other = values
return first - calculate_sum(other)
Note the *-marker in front of the other variable. When assigning a list two multiple variables, they are "unpacked" by python. Thus a, b, c = [0, 1, 2] would assign 0 to a and so on. However, when we do a, b = [0, 1, 2], then python complains because there are too many variables to unpack in the list (3 > 2). With the asterisk we simply tell python to put all other values, not used so far, into this special variable again. a, b, *rest = [1, 2, 3, 4, 5, 6] is also possible.
Ok, computing the product is as easy as summing up, just replace += by *= in the method. And for the quotient we can do the same as for the difference, since a * 1/b * 1/c * 1/d = a / (b*c*d). However, note that if the divisor is zero, python will raise an Error DivisionByZero, as this is not legal. Also, the result of the method is float and no longer int.

Python list first n entries in a custom base number system

I am sorry if the title is a misnomer and/or doesn't properly describe what this is all about, you are welcome to edit the title to make it clear once you understand what this is about.
The thing is very simple, but I find it hard to describe, this thing is sorta like a number system, except it is about lists of integers.
So we start with a list of integers with only zero, foreach iteration we add one to it, until a certain limit is reached, then we insert 1 at the start of the list, and set the second element to 0, then iterate over the second element until the limit is reached again, then we add 1 to the first element and set the second element 0, and when the first element reaches the limit, insert another element with value of 1 to the start of the list, and zero the two elements after it, et cetera.
And just like this, when a place reaches limit, zero the place and the places after it, increase the place before it by one, and when all available places reach limit, add 1 to the left, for example:
0
1
2
1, 0
1, 1
1, 2
2, 0
2, 1
2, 2
1, 0, 0
The limit doesn't have to be three.
This is what I currently have that does something similar to this:
array = []
for c in range(26):
for b in range(26):
for a in range(26):
array.append((c, b, a))
I don't want leading zeroes but I can remove them, but I can't figure out how to do this with a variable number of elements.
What I want is a function that takes two arguments, limit (or base) and number of tuples to be returned, and returns the first n such tuples in order.
This must be very simple, but I just can't figure it out, and Google returns completely irrelevant results, so I am asking for help here.
How can this be done? Any help will truly be appreciated!
Hmm, I was thinking about something like this, but very unfortunately I can't make it work, please help me figure out why it doesn't work and how to make it work:
array = []
numbers = [0]
for i in range(1000):
numbers[-1] += 1
while 26 in numbers:
index = numbers.index(26)
numbers[index:] = [0] * (len(numbers) - index)
if index != 0:
numbers[index - 1] += 1
else:
numbers.insert(0, 1)
array.append(numbers)
I don't quite understand it, my testing shows everything inside the loop work perfectly fine outside the loop, the results are correct, but it just simply magically will not work in a loop, I don't know the reason for this, it is very strange.
I discovered the fact that if I change the last line to print(numbers) then everything prints correctly, but if I use append only the last element will be added, how so?
from math import log
def number_to_base(n,base):
number=[]
for digit in range(int(log(n+0.500001,base)),-1,-1):
number.append(n//base**digit%base)
return number
def first_numbers_in_base(n,base):
numbers=[]
for i in range(n):
numbers.append(tuple(number_to_base(i,base)))
return numbers
#tests:
print(first_numbers_in_base(10,3))
print(number_to_base(1048,10))
print(number_to_base(int("10201122110212",3),3))
print(first_numbers_in_base(25,10))
I finally did it!
The logic is very simple, but the hard part is to figure out why it won't work in a loop, turns out I need to use .copy(), because for whatever reason, doing an in-place modification to a list directly modifies the data reside in its memory space, such behavior modifies the same memory space, and .append() method always appends the latest data in a memory space.
So here is the code:
def steps(base, num):
array = []
numbers = [0]
for i in range(num):
copy = numbers.copy()
copy[-1] += 1
while base in copy:
index = copy.index(base)
copy[index:] = [0] * (len(copy) - index)
if index != 0:
copy[index - 1] += 1
else:
copy.insert(0, 1)
array.append(copy)
numbers = copy
return array
Use it like this:
steps(26, 1000)
For the first 1000 lists in base 26.
Here is a a function, that will satisfy original requirements (returns list of tuples, first tuple represents 0) and is faster than other functions that have been posted to this thread:
def first_numbers_in_base(n,base):
if n<2:
if n:
return [(0,)]
return []
numbers=[(0,),(1,)]
base-=1
l=-1
num=[1]
for i in range(n-2):
if num[-1]==base:
num[-1]=0
for i in range(l,-1,-1):
if num[i]==base:
num[i]=0
else:
num[i]+=1
break
else:
num=[1]+num
l+=1
else:
num[-1]+=1
numbers.append(tuple(num))#replace tuple(num) with num.copy() if you want resutl to contain lists instead of tuples.
return numbers

Averaging results from a list of lists where every nth list is a reptition

So I wrote a model that computes results over various parameters via a nested loop. Each computation returns a list of len(columns) = 10 elements, which is added to a list of lists (res).
Say I compute my results for some parameters len(alpha) = 2, len(gamma) = 2, rep = 3, where rep is the number of repetitions that I run. This yields results in the form of a list of lists like this:
res = [ [elem_1, ..., elem_10], ..., [elem_1, ..., elem_10] ]
I know that len(res) = len(alpha) * len(gamma) * repetitions = 12 and that each inner list has len(columns) = 10 elements. I also know that every 3rd list in res is going to be a repetition (which I know from the way I set up my nested loops to iterate over all parameter combinations, in fact I am using itertools).
I now want to average the result list of lists. What I need to do is to take every (len(res) // repetitions) = 4th list , add them together element-wise, and divide by the number of repetitions (3). Sounded easier than done, for me.
Here is my ugly attempt to do so:
# create a list of lists of lists, where the inner list of lists are lists of the runs with the identical parameters alpha and gamma
res = [res[i::(len(res)//rep)] for i in range(len(res)//rep)]
avg_res = []
for i in res:
result = []
for j in (zip(*i)):
result.append(sum(j))
avg_res.append([i/repetitions for i in result])
print(len(result_list), avg_res)
This actually yields, what I want, but it surely is not the pythonic way to do it. Ugly as hell and 5 minutes later I can hardly make sense of my own code...
What would be the most pythonic way to do it? Thanks in advance!
In some cases a pythonic code is a matter of style, one of its idioms is using list comprehension instead of loop so writing result = [sum(j) for j in (zip(*i))] is simpler than iterating over zip(*i).
On the other hand nested list comprehension looks more complex so don't do
avg_res = [[i/repetitions for i in [sum(j) for j in (zip(*j))]] for j in res]
You can write:
res = [res[i::(len(res)//rep)] for i in range(len(res)//rep)]
avg_res = []
for i in res:
result = [sum(j) for j in (zip(*i))]
avg_res.append([i/repetitions for i in result])
print(len(result_list), avg_res)
Another idiom in Programming in general (and in python in particular) is naming operations with functions, and variable names, to make the code more readable:
def sum_columns(list_of_rows):
return [sum(col) for col in (zip(*list_of_rows))]
def align_alpha_and_gamma(res):
return [res[i::(len(res)//rep)] for i in range(len(res)//rep)]
aligned_lists = align_alpha_and_gamma(res)
avg_res = []
for aligned_list in aligned_lists:
sums_of_column= sum_columns(aligned_list)
avg_res.append([sum_of_column/repetitions for sum_of_column in sums_of_column])
print(len(result_list), avg_res)
Off course you can choose better names according to what you want to do in the code.
It was a bit hard to follow your instructions, but as I caught, you attempt to try sum over all element in N'th list and divide it by repetitions.
res = [list(range(i,i+10)) for i in range(10)]
N = 4
repetitions = 3
average_of_Nth_lists = sum([num for iter,item in enumerate(res) for num in item if iter%N==0])/repetitions
print(average_of_Nth_lists)
output:
85.0
explanation for the result: equals to sum(0-9)+sum(4-13)+sum(8-17) = 255 --> 255/3=85.0
created res as a list of lists, iterate over N'th list (in my case, 1,5,9 you can transform it to 4,8 etc if that what you are wish, find out where in the code or ask for help if you don't get it), sum them up and divide by repetitions

Filling a list inside a for loop in python

I am trying to make a vector out of two different ones as shown in the piece of code below.
However, I get a list out of range exception on the 5th line the first time the code goes in the for loop.
What am I doing wrong?
def get_two_dimensional_vector(speeds, directions):
vector = []
for i in range(10):
if (i % 2 == 0):
vector[i/2][0] = speeds[i/2]
else :
vector[i/2 - 1/2][1] = directions[i/2 - 1/2]
You can't use a Python list this way. It's not like a C array with a
predefined length. If you want to add a new element, you have to use the
append method or something.
Aside from that, you're also using a second index, implying that the
elements of vector are themselves lists or dicts or something, before
they've even been assigned.
It looks like you want to convert speeds and directions to a
two-dimensional list. So, first, here's how to do that with a loop. Note
that I've removed the fixed-size assumption you were using, though the
code still assumes that speeds and directions are the same size.
def get_two_dimensional_vector(speeds, directions):
vector = []
for i in range(len(speeds)):
vector.append([speeds[i], directions[i]])
return vector
speeds = [1, 2, 3]
directions = [4, 5, 6]
v = get_two_dimensional_vector(speeds, directions)
print(v)
Now, the Pythonic way to do it.
print(zip(speeds, directions))

Python For Loop list

I'm new to Python and I'm currently trying to work some lists. I have a list with objects with each object having two attributes:
for s in objlist:
s.attribute1
s.attribute2
The problem with this is that I want to read and process two objects at a time, like this:
s1.attribute1
s1.attribute2
s2.attribute1
s2.attribute2
And then have the loop "skip" the next object as it was already processed.
In Java it'd be something like this:
for [int i = 0, i<list.length, i = i+2]
list[i].attribute1
list[i].attribute2
list[i+1]attribute1
list[i+1]attribute2
And then just allow it to iterate with i+2 to the next pair of objects.
How do I do this in Python?
You can do the same in Python also,
for i in xrange(0, len(seq), 2):
seq[i].attribute1
seq[i].attribute2
seq[i+1].attribute1
seq[i+1].attribute2
xrange will give a range of numbers one at a time. It starts with 0, ends at length of seq and the step value will be 2.
Providing your list is evenly sized:
for x, y in izip(objlist[::2], objlist[1::2]):
x.attribute1
y.attribute2
You can try this:
x = [1,2,3,4,5,6,7,8,9,0]
i = 0
while i < len(x):
print x[i], x[i+1]
i += 2
and your x list must be an even size

Categories

Resources