newbie question: for loop within for loop confusion

T

takayuki

Hi,

I'm studying python via the exellent book "How to think like a python
programmer" by Allen Downey.

Noob question follows...

animals.txt is a list of animals, each on a separate line: "aardvard,
bat, cat, dog, elephant, fish, giraffe, horse, insect, jackelope"

I want to loop through the list of words and print words that don't
have any "avoid" letter in them.

def hasnolet(avoid):
fin = open('animals.txt')
for line in fin:
word = line.strip()
for letter in avoid:
if letter in word:
break
else:
print word

hasnolet('abcd')

Why doesn't the function above work? It returns:

dog
dog
dog
fish
fish
fish
fish
horse
horse
horse
horse
inchworm
inchworm


thanks for any help.

takayuki
 
R

Roy Smith

takayuki said:
Hi,

I'm studying python via the exellent book "How to think like a python
programmer" by Allen Downey.

Noob question follows...

animals.txt is a list of animals, each on a separate line: "aardvard,
bat, cat, dog, elephant, fish, giraffe, horse, insect, jackelope"

I want to loop through the list of words and print words that don't
have any "avoid" letter in them.

def hasnolet(avoid):
fin = open('animals.txt')
for line in fin:
word = line.strip()
for letter in avoid:
if letter in word:
break
else:
print word

hasnolet('abcd')

I could give you a fish, or I could teach you to fish. I'd rather do the
latter. So...

Take your inner loop:
for letter in avoid:
if letter in word:
break
else:
print word

and instrument it so you can see exactly what's happening. Try something
like:
for letter in avoid:
print "letter = %s" % letter
if letter in word:
print "it's in word"
break
else:
print "no it's not"
print word

and see if that helps.
 
J

John Salerno

takayuki wrote:

for letter in avoid:
if letter in word:
break
else:
print word

Take the word 'dog', for example. What the above loop is doing is
basically this:

1. for letter in avoid uses 'a' first
2. is 'a' in 'dog'?
3. no, so it prints 'dog'
4. go back to for loop, use 'b'
5. is 'b' in 'dog'?
6. no, so it prints 'dog' again
7. go back to for loop.....

Since it goes sequentially through 'abcd', it will say that the first
three letters are not in 'dog', and therefore print it three times. Then
it finally sees that 'd' *is* in dog, so it skips it the fourth time
through the loop.
 
T

takayuki

Thanks to everyone for the excellent advice.

Roy: I did as you suggested and could see after staring at the output
for awhile what was going on. The print statements really helped to
put a little light on things. Yes, I agree that "learning to fish" is
the best way.

John: There were two "inchworms" because "c" is in "inchworm" so it
shouldn't print. Thanks for your detailed description of the for
loop.

The Saint: i'll check out the word = line.split() command.


After much flailing about, here's a loop that is working:

def hasnolet2(avoid):
fin = open('animals.txt')
for line in fin:
word = line.strip()

length = len(avoid)
x = 0
noprint = 0

while length -1 >= x:
if avoid[x] in word:
noprint = noprint + 1
x = x + 1

if noprint == 0:
print word

hasnolet2('abcd')

which should return:
fish
horse


hasnolet2('abcd')
 
P

Paul Hankin

def hasnolet2(avoid):
        fin = open('animals.txt')
        for line in fin:
                word = line.strip()

        length = len(avoid)
        x = 0
        noprint = 0

        while length -1 >= x:
                if avoid[x] in word:
                        noprint = noprint + 1
                x = x + 1

        if noprint == 0:
                print word

There seems to be an indendation problem (presumably the code from
length = len(avoid) onwards should be inside the loop). But apart from
that, we can try to make this more 'pythonic'.

First, python has a 'for' statement that's usually better than using
while. We use the 'range' function that produces the numbers 0, 1, ...
length - 1, and x takes the value of these in turn.

Here's the last bit of your code rewritten like this:

noprint = 0

for x in range(length):
if avoid[x] in word:
noprint += 1

if noprint == 0:
print word

But better, rather than using 'x' as an index, we can loop over
letters in avoid directly. I've changed 'noprint' to be a boolean
'should_print' too here.

should_print = True
for letter in avoid:
if letter in word:
should_print = False

if should_print:
print word

We can eliminate 'should_print' completely, by using 'break' and
'else'. A break statement in a loop causes the loop to end. If the
loop doesn't break, the 'else' code is run when the loop's over.

for letter in avoid:
if letter in word:
break
else:
print word

This is almost the same as your original code, but the 'else' is
attached to the 'for' rather that the 'if'!

Finally, in Python 2.5 you can write this:

if not any(letter in word for letter in avoid):
print word

I think this is the best solution, as it's readable and short.
 
J

John Salerno

takayuki said:
John: There were two "inchworms" because "c" is in "inchworm" so it
shouldn't print. Thanks for your detailed description of the for
loop.

lol, I even sat there looking at the word and said to myself "ok, it doesn't
contain any of the four letters" :)
 
J

John Salerno

takayuki said:
fin = open('animals.txt')
for line in fin:

You can write this as:

for line in open('animals.txt'):
#do stuff

Of course, you can't explicitly close the file this way, but that probably
doesn't matter. Another way, I think, is to wrap the for loops in this:

with open('animals.txt') as file:
#for loop stuff here
 
M

MRAB

def hasnolet2(avoid):
fin = open('animals.txt')
for line in fin:
word = line.strip()
length = len(avoid)
x = 0
noprint = 0
while length -1 >= x:
if avoid[x] in word:
noprint = noprint + 1
x = x + 1
if noprint == 0:
print word

There seems to be an indendation problem (presumably the code from
length = len(avoid) onwards should be inside the loop). But apart from
that, we can try to make this more 'pythonic'.

First, python has a 'for' statement that's usually better than using
while. We use the 'range' function that produces the numbers 0, 1, ...
length - 1, and x takes the value of these in turn.

Here's the last bit of your code rewritten like this:

noprint = 0

for x in range(length):
if avoid[x] in word:
noprint += 1

if noprint == 0:
print word

But better, rather than using 'x' as an index, we can loop over
letters in avoid directly. I've changed 'noprint' to be a boolean
'should_print' too here.

should_print = True
for letter in avoid:
if letter in word:
should_print = False

if should_print:
print word

We can eliminate 'should_print' completely, by using 'break' and
'else'. A break statement in a loop causes the loop to end. If the
loop doesn't break, the 'else' code is run when the loop's over.

for letter in avoid:
if letter in word:
break
else:
print word

This is almost the same as your original code, but the 'else' is
attached to the 'for' rather that the 'if'!

Finally, in Python 2.5 you can write this:

if not any(letter in word for letter in avoid):
print word

I think this is the best solution, as it's readable and short.
Alternatively, you could use sets:

if not(set(word) & set(avoid)):
print word

(parentheses added for clarity.)
 
T

Thomas Hill

def hasnolet(avoid):
fin = open('animals.txt')
for line in fin:
word = line.strip()
for letter in avoid:
if letter in word:
break
else:
print word

You're using the split command correctly, but you're not filtering
correctly. Consider this:

---begin---
fin = open('animals.txt')
"\n".join(["%s" % line for line in fin if len(line.strip('abcd')) ==
len(line)])
----end----

Let's go slow.

"\n".join([...])

1. Take everything that is in the following list, and print each one
with a carriage return appended to it.

"\n".join(["%s" % line for line in fin ...])

2. For each line in fin, create a string that only consists of what
currently in the line variable, using string substitution.

"\n".join(["%s" % line for line in fin if len(line.strip('abcd')) ==
len(line)])

3. Only do #2 if the length of the line after stripping out the
unnecessary characters is the same length as the line originally. This
way we filter out the lines we don't want. If we wanted the lines that
have been filtered, we can change "==" to "!=" or "<=".

Now, I read "Dive Into Python" first, which through these early on in
the book. If your eyes cross looking at this, write it down and read
it again after you get a little farther into the book you're reading
 
T

Thomas Hill

def hasnolet(avoid):
fin = open('animals.txt')
for line in fin:
word = line.strip()
for letter in avoid:
if letter in word:
break
else:
print word

You're using the split command correctly, but you're not filtering
correctly. Consider this:

---begin---
fin = open('animals.txt')
"\n".join(["%s" % line for line in fin if len(line.strip('abcd')) ==
len(line)])
----end----

Let's go slow.

"\n".join([...])

1. Take everything that is in the following list, and print each one
with a carriage return appended to it.

"\n".join(["%s" % line for line in fin ...])

2. For each line in fin, create a string that only consists of what
currently in the line variable, using string substitution.

"\n".join(["%s" % line for line in fin if len(line.strip('abcd')) ==
len(line)])

3. Only do #2 if the length of the line after stripping out the
unnecessary characters is the same length as the line originally. This
way we filter out the lines we don't want. If we wanted the lines that
have been filtered, we can change "==" to "!=" or "<=".

Now, I read "Dive Into Python" first, which through these early on in
the book. If your eyes cross looking at this, write it down and read
it again after you get a little farther into the book you're reading

Guh, no, I'm reading the description of strip wrong. Fooey. Anyone
else able to one line it?
 
T

takayuki

Paul,

Thank you for the informative reply.

Yes, I created the indent problem when manually copying the original
script when I posted. (I'm using an old laptop to study python and
posting here using the desktop.)

Your examples really helped. Last night I played with using a for
loop instead of a while loop and got it working, but your examples
really clarified things.

This loop was particularly interesting because I didn't know the else
could be attached to the for. Attaching it to the for solved a lot of
problems.

for letter in avoid:
if letter in word:
break
else:
print word


I've printed out this whole thread and will be playing with your and
others' solutions.

I love this one for its conciseness, but will have to play with it to
get my head around it.

if not any(letter in word for letter in avoid):
print word

Thanks again.

takayuki
 
T

takayuki

def hasnolet(avoid):
fin = open('animals.txt')
for line in fin:
word = line.strip()
for letter in avoid:
if letter in word:
break
else:
print word

You're using the split command correctly, but you're not filtering
correctly. Consider this:

---begin---
fin = open('animals.txt')
"\n".join(["%s" % line for line in fin if len(line.strip('abcd')) ==
len(line)])
----end----

Let's go slow.

"\n".join([...])

1. Take everything that is in the following list, and print each one
with a carriage return appended to it.

"\n".join(["%s" % line for line in fin ...])

2. For each line in fin, create a string that only consists of what
currently in the line variable, using string substitution.

"\n".join(["%s" % line for line in fin if len(line.strip('abcd')) ==
len(line)])

3. Only do #2 if the length of the line after stripping out the
unnecessary characters is the same length as the line originally. This
way we filter out the lines we don't want. If we wanted the lines that
have been filtered, we can change "==" to "!=" or "<=".

Now, I read "Dive Into Python" first, which through these early on in
the book. If your eyes cross looking at this, write it down and read
it again after you get a little farther into the book you're reading

Thomas,

thanks for the reply.

I'm early on in my python adventure so I'm not there yet on the strip
command nuances. I'm reading "How to think like a python
programmer" first. It's great.

Then "Learning python". I've read parts of Dive into Python and will
work through it fully when I'm a little farther along.

takayuki
 
J

John Salerno

takayuki said:
I'm early on in my python adventure so I'm not there yet on the strip
command nuances. I'm reading "How to think like a python
programmer" first. It's great.

Then "Learning python". I've read parts of Dive into Python and will
work through it fully when I'm a little farther along.

Yeah, I really recommend Learning Python for getting the basics first.
It's very thorough in that regard. Dive Into Python is *not* for
beginners. I'm sorry if people disagree, but it's just not.
 
D

David

takayuki said:
Paul,

Thank you for the informative reply.

Yes, I created the indent problem when manually copying the original
script when I posted. (I'm using an old laptop to study python and
posting here using the desktop.)

Your examples really helped. Last night I played with using a for
loop instead of a while loop and got it working, but your examples
really clarified things.

This loop was particularly interesting because I didn't know the else
could be attached to the for. Attaching it to the for solved a lot of
problems.

for letter in avoid:
if letter in word:
break
else:
print word


I've printed out this whole thread and will be playing with your and
others' solutions.

I love this one for its conciseness, but will have to play with it to
get my head around it.

if not any(letter in word for letter in avoid):
print word

Thanks again.

takayuki

takayuki,

Could you post the complete script.
thanks
david
 
G

Gabriel Genellina

Yeah, I really recommend Learning Python for getting the basics first.
It's very thorough in that regard. Dive Into Python is *not* for
beginners. I'm sorry if people disagree, but it's just not.

Sure, the author himself says so at the very beginning in http://www.diveintopython.org "Dive Into Python is a Python book for experienced programmers."
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top