python list generation/saving bug - python

I am trying to make program that prints all the possible combinations for a to zzz. I tried to add a save state feature, and it works fine but there is this bug.
Let's say I interrupted the program when it printed something like e. When I execute the program again, it works fine until z but after z instead of printing aa it prints ba and continues from ba. This happens right after it prints zz too. it prints baa instead of aaa. How can I fix this?
Here is what I did so far:
import pickle,os,time
alphabet="abcdefghijklmnopqrstuvwxyz"
try:
if os.path.isfile("save.pickle")==True:
with open("save.pickle","rb") as f:
tryn=pickle.load(f)
for i in range(3):
a=[x for x in alphabet]
for j in range(i):
a=[x+i for x in alphabet for i in a]
b=a[tryn:]
for k in b:
print(k)
time.sleep(0.01)
tryn+=1
else:
tryn=0
for i in range(3):
a=[x for x in alphabet]
for j in range(i):
a=[x+i for x in alphabet for i in a]
for k in a:
print(k)
tryn+=1
time.sleep(0.01)
except KeyboardInterrupt:
with open("save.pickle","wb") as f:
pickle.dump(tryn,f)

If you're using python2, or python3 as the tag suggests, this exists in the standard library already. See itertools, product py2, and product py3, for a simple way to solve this problem.

for i in range(3):
a=[x for x in alphabet]
for j in range(i):
a=[x+i for x in alphabet for i in a]
b=a[tryn:]
Here's your bug. You skip the first tryn strings of every length, rather than just the first tryn strings. This would be easier to recognize in the output if it weren't for the following:
for k in b:
print(k)
time.sleep(0.01)
tryn+=1
You modify tryn, the number of things you're skipping. When you print out length-2 strings, you skip a number of them equal to the number of length-1 strings. When you print out length-3 strings, you skip a number of them equal to the number of length-2 strings. If tryn were bigger than the number of length-1 strings, you would skip even more.

your problem is almost certainly here:
a=[x for x in alphabet]
for j in range(i):
a=[x+i for x in alphabet for i in a]
Perhaps you shouldn't assign the in-loop value to a, but instead use a different name? Otherwise, you are changing what you use every time through the loop....
Edit: More detail. So, technically user2357112's answer is more correct, but I'm amending mine. The initial answer was just from a quick reading, so the other answer is close to the original intent. But, the original version is inefficient (for more reasons than not using product :), since you are generating the inner loops more than once. So let's walk through why this is a bad idea, as an educational exercise:
Initial algorithm:
for i in range(n):
assign a to alphabet
for j in range(i):
i times, we rewrite a to be all combinations of the current set against the alphabet.
Note that for this algorithm, to generate the length(n) product, we have to generate all previous products length(n-1), length(n-2), ..., length(1). But you aren't saving those.
You'd be better off doing something like this:
sum_list = alphabet[:]
#get a copy
product_list = alphabet[:]
#Are we starting at 0, or 1? In any case, skip the first, since we preloaded it
for i in range(1, n):
# Your existing list comprehension was equivalent here, and could still be used
# it MIGHT be faster to do '%s%s'%(x,y) instead of x+y... but maybe not
# with these short strings
# This comprehension takes the result of the last iteration, and makes the next iteration
product_list = [x+y for x,y in product(product_list, alphabet)]
# So product list is JUST the list for range (n) - i.e. if we are on loop 2, this
# is aaa...zzz. But you want all lengths together. So, as you go, add these
# sublists to a main list.
sum_list.extend(product_list)
Overall, you are doing a lot less work.
Couple other things:
You're using i as a loop variable, then re-using it in the loop comprehension. This is conflicting, and probably not working the way you'd expect.
If this is to learn how to write save/restore type apps... it's not a good one. Note that the restore function is re-calculating every value to be able to get back where it left off - if you could rewrite this algorithm to write more information out to the file (such as the current value of product_list) and make it more generator-like, then it will actually work more like a real-world example.

Here is how I would suggest solving this problem in Python. I didn't implement the save state feature; this sequence is not a really long one and your computer should be able to produce this sequence pretty fast, so I don't think it is worth the effort to try to make it cleanly interruptable.
import itertools as it
def seq(alphabet, length):
for c in range(1, length+1):
for p in it.product(alphabet, repeat=c):
yield ''.join(p)
alphabet="abcdefghijklmnopqrstuvwxyz"
for x in seq(alphabet, 3):
print(x)
If you really wanted to, you could make a one-liner using itertools. I think this is too hard to read and understand; I prefer the above version. But this does work and will be somewhat faster, due to the use of itertools.chain and itertools.imap() rather than a Python for loops.
import itertools as it
def seq(alphabet, length):
return it.imap(''.join, it.chain.from_iterable(it.product(alphabet, repeat=c) for c in range(1, length+1)))
alphabet="abcdefghijklmnopqrstuvwxyz"
for x in seq(alphabet, 3):
print(x)
In Python 3.x you could just use map() rather than itertools.imap().

Related

"List comprehensions" directly in for loop line

Is it possible to add a "list comprehensions" directly in for loop line? Current code:
cubes = [i**3 for i in range(5)]
for value in cubes:
print(value)
I want to write something like (this obviously doesn't work):
for value in cubes = [i**3 for i in range(5)]:
print(value)
or
cubes = [ ]
for value in cubes = [i**3 for i in range(5)]:
print(value)
You can simply just put the expression in the loop as is:
for value in [i**3 for i in range(5)]:
print(value)
output:
0
1
8
27
64
If you want both the list cubes for later in the code and want to print, you can do everything in the list comprehension by exploiting the fact that print returns None:
cubes = [print(i**3) or i**3 for i in range(5)]
Is this recommended? NO! It's a clear breach of "The Zen of Python" (PEP20).
If n for your range is sufficiently large, using a list comprehension for your loop will hang before it even begins. You might want to abstract away the loop filtering by using yield:
def create_numbers(n):
for x in range(n):
yield x**3
for i in create_numbers(5):
print(i)
yield will give you those values one at a time, only calculating the next value when next() is invoked by the loop. This way your loop looks nicer and doesn't iterate over that list 2*n times
I strongly suggest you keep the code you have now:
cubes = [i**3 for i in range(5)]
for value in cubes:
print(value)
It's clear what it does, and it is correct. There's little reason to eliminate a single line of code at the risk of changing that.
That said, Python 3.8 will introduce assignment expressions, which can (if I am reading PEP-572 correctly; no similar examples appear there) be used to eliminate that line of code:
for value in cubes := [i**3 for i in range(5)]:
print(value)
Time will tell if this will be considered good, or at least acceptable, style.

Why does 'while' cause the program to run longer in Python

I wrote a program to find out the primes in a list of numbers, just so practice formatting and all. Here is my code:
from math import *
#Defining range to be checked
a = range(1,10)
#Opening empty list
b = []
#Wilsons method for primes
for n in a:
if ((factorial(n-1))+1)%n == 0:
b.append(n)
This code runs without issue and fairly fast, at least at this stage of use. However, when I include a while statement(see below), it is substantially slower.
from math import *
#Defining range to be checked
a = range(1,10)
#Opening empty list
b = []
#Wilson't method for primes
for n in a:
while n>1:
if ((factorial(n-1))+1)%n == 0:
b.append(n)
Could anyone explain why that would be the case?
n.b: I know there are more efficient method to find primes. I am just practicing formatting and all, although I don't mind improving my code.
edit: mistaken addition of less than symbol rather than the appropriate greater than symbol. Corrected.
As pointed out by several people your code will result in an infinite loop as the value of n does not change within your while-loop.
You are probably not looking for a while loop in the first place. It should be sufficient to use the for loop without the first iteration (n = 1). If you insist on including n=1, using an if statement is a possible workaround:
a=range(1,10)
b=[]
for n in a:
if n>1:
if ((factorial(n-1))+1)%n == 0:
b.append(n)

Using comprehensions instead of a for loop

The following is a simplified example of my code.
>>> def action(num):
print "Number is", num
>>> items = [1, 3, 6]
>>> for i in [j for j in items if j > 4]:
action(i)
Number is 6
My question is the following: is it bad practice (for reasons such as code clarity) to simply replace the for loop with a comprehension which will still call the action function? That is:
>>> (action(j) for j in items if j > 2)
Number is 6
This shouldn't use a generator or comprehension at all.
def action(num):
print "Number is", num
items = [1, 3, 6]
for j in items:
if j > 4:
action(i)
Generators evaluate lazily. The expression (action(j) for j in items if j > 2) will merely return a generator expression to the caller. Nothing will happen in it unless you explicitly exhaust it. List comprehensions evaluate eagerly, but, in this particular case, you are left with a list with no purpose. Just use a regular loop.
This is bad practice. Firstly, your code fragment does not produce the desired output. You would instead get something like: <generator object <genexpr> at 0x03D826F0>.
Secondly, a list comprehension is for creating sequences, and generators a for creating streams of objects. Typically, they do not have side effects. Your action function is a prime example of a side effect -- it prints its input and returns nothing. Rather, a generator should for each item it generates, take an input and compute some output. eg.
doubled_odds = [x*2 for x in range(10) if x % 2 != 0]
By using a generator you are obfuscating the purpose of your code, which is to mutate global state (printing something), and not to create a stream of objects.
Whereas, just using a for loop makes the code slightly longer (basically just more whitespace), but immediately you can see that the purpose is to apply function to a selection of items (as opposed to creating a new stream/list of items).
for i in items:
if i < 4:
action(i)
Remember that generators are still looping constructs and that the underlying bytecode is more or less the same (if anything, generators are marginally less efficient), and you lose clarity. Generators and list comprehensions are great, but this is not the right situation for them.
While I personally favour Tigerhawk's solution, there might be a middle ground between his and willywonkadailyblah's solution (now deleted).
One of willywonkadailyblah's points was:
Why create a new list instead of just using the old one? You already have the condition to filter out the correct elements, so why put them away in memory and come back for them?
One way to avoid this problem is to use lazy evaluation of the filtering i.e. have the filtering done only when iterating using the for loop by making the filtering part of a generator expression rather than a list comprehension:
for i in (j for j in items if j > 4):
action(i)
Output
Number is 6
In all honesty, I think Tigerhawk's solution is the best for this, though. This is just one possible alternative.
The reason that I proposed this is that it reminds me a lot of LINQ queries in C#, where you define a lazy way to extract, filter and project elements from a sequence in one statement (the LINQ expression) and can then use a separate for each loop with that query to perform some action on each element.

What is a set? And what does "a for a in i" do?

I came across these constructs and I'm not quite sure what they do. Can someone explain?
setx = set([a for a in i])
sety = set([y for y in j])
Code, for context
a = int(input())
for i in range(a):
i = (input())
j = (input())
setx = set([a for a in i])
sety = set([y for y in j])
if setx.intersection(sety) == set():
print("NO")
else:
print("YES")
[a for a in i] is a list comprehension. It's basically a concise way to make a list.
They can be really useful, or they can be source of much unreadable code, or both. The full syntax is
[f(i) for i in iterator if conditional(i)]
examples:
List of squares: [i**2 for i in range(n)]
List of squares not divisible by 5: [i**2 for i in range(n) if i**2 % 5 =! 0]
And as for set: Set is a very useful data type in python. It's basically a dictionary without the values. All elements must be unique and hashable, and sets do not store order, but checking to see if an object is in a set is not dependent on the length of the set.
In this case, your code is probably using sets to make figuring out if the two inputs share any commonalities faster and easier to write.
(Also, finally: Uh, as posted, I don't really know what your code does, but I can be pretty sure it doesn't what it wants to. If I were to rewrite it, it'd be something like
a = int(input())
setx = set() #initializing empty sets
sety = set()
for _ in range(a): #underscore often used when you don't care about what you're iterating over, just that you iterate a certain amount of times.
setx.add(input()) #Previously, you'd just get the last input
sety.add(input()) #I doubt that's what you wanted
if setx.intersection(sety): #no need to compare to empty set. if set() will evaluate as false
print("NO")
else:
print("YES")
)
Set is build-in data type: a collection of unordered unique elements, strong on unique.
For more specifics see the amazing python Docs: For example See [https://docs.python.org/3.5/library/stdtypes.html?highlight=set#set]
So using set() over that list comprehension it is removing duplicates from the list.

Python - I dont know how to min works in this code

My teacher gave me this code:
def n_o_c(Q,v):
M=[None]*(Q+1)
m={}
M[0]=0
for q in xrange(1,Q+1):
M[q]=min(M[q-a]+1 for a in v if q-a>=0)
return M[Q],m
print n_o_c(18,[1,2,5])
1st I explain the script, It's a supposed coin machine and I have to know how much coins I need for pay Q quantity having v coins(we have to do less coins as posible IE for 18 3x5 coin 1x2 coin and 1x1 coin)
I dont understand what does that M[q] line, I've tried to print M and the result I had was every number from 1 to 18 how many coins it needs to do that number.
M=[0,1,1,2,2,3,2,2,3,3,2,3,3,4,4,3,4,4,5]
q=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18]
Can someone explain me how that min works?
I've already done it (I know its not the good way to solve the exercise but I dont know a better way for). Solved:
def n_o_c(Q,v):
M=[None]*(Q+1)
m={}
M[0]=0
for q in xrange(1,Q+1):
M[q]=min(M[q-a]+1 for a in v if q-a>=0)
monedas=0
total=Q
m=[] # did this to change dictionary to array
while(monedas<M[Q]):
for a in v[::-1]:
if total-a >= 0:
total = total-a
monedas = monedas +1
m.append(a)
break #I forget this break
return M[Q],m
print n_o_c(18,[1,2,5])
The min function is the easy part:
Return the smallest item in an iterable
The tricky bit is, what's with that iterable?
M[q]=min(M[q-a]+1 for a in v if q-a>=0)
That (M[q-a]+1 for a in v if q-a>=0) is called a generator expression; more generally, it's a type of comprehension.
Start at List Comprehensions in the official tutorial to learn how comprehensions in general work, then Iterators and the following two sections (Generators and Generator Expressions) to learn how generator expressions are different.*
But I can summarize it here, at least enough to get you started.
First, a list comprehension:
[M[q-a]+1 for a in v if q-a>=0]
What this means is that you want to build a list, exactly as if you'd unrolled it into a loop like this:
value = []
for a in v:
if q-a>=0:
value.append(M[q-a]+1)
M[q] = min(value)
Or, more intuitively, try reading it aloud: a list of every M[q-a]+1 for each a in v if q-a>=0 makes sense as an English sentence, and means exactly the same thing as the Python. (If you have a math background, you might want to think of it in terms of set displays instead, but I'll assume you don't.)
A generator expression does the same thing, except that instead of building a list, it creates the values on demand as you iterate over it. You can think of this as sort of a magic list that doesn't waste memory or time for now. To spell it in Python, just turn the square brackets [] into parentheses () (which you can leave off in this case, because there are already parentheses from the min call). To read it aloud, just leave of the "a list of" part.
* And once you get that, if you want to learn more, take a look at the itertools module, read Generator Tricks for System Programmers by David Beazley and google for Greg Ewing's presentations on generators.
Why is m not being used in your teacher's code ?
Here is a solution:
Sort the v list and divide by it by each dimension to get num coins and continue with change remaining.
def n_o_c(Q,v):
m = {}
for dimension in sorted(v, reverse= True):
num_coins = int(Q/dimension)
## if Q is not divisible by the dimension, go to the next dimension
if num_coins != 0:
## if Q is divisible, store num_coins in the dictionary
m[dimension] = num_coins
## Get change remaining by modulo function and store it as remaining Q that needs to be broken down
Q = Q % dimension
return m
print n_o_c(18,[1,2,5])
Should print:
{1: 1, 2: 1, 5: 3}

Categories

Resources