Velocity Reviews > newbie question: for loop within for loop confusion

# newbie question: for loop within for loop confusion

takayuki
Guest
Posts: n/a

 06-16-2008
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

Roy Smith
Guest
Posts: n/a

 06-16-2008
In article
<(E-Mail Removed)>,
takayuki <(E-Mail Removed)> wrote:

> 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...

> 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.

TheSaint
Guest
Posts: n/a

 06-16-2008
On 09:23, lunedÃ¬ 16 giugno 2008 takayuki wrote:

> word = line.strip()

Try
word= line.split()

and at the end of the loop add one more print to go to new line.
--
Mailsweeper Home : http://it.geocities.com/call_me_not_now/index.html

John Salerno
Guest
Posts: n/a

 06-16-2008
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.

John Salerno
Guest
Posts: n/a

 06-16-2008
takayuki wrote:

> inchworm
> inchworm

P.S. Why does 'inchworm' only print twice? Or is that not the full output?

takayuki
Guest
Posts: n/a

 06-16-2008
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')

Paul Hankin
Guest
Posts: n/a

 06-16-2008
On Jun 16, 2:35*pm, takayuki <(E-Mail Removed)> wrote:
> 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.

--
Paul Hankin

John Salerno
Guest
Posts: n/a

 06-16-2008
"takayuki" <(E-Mail Removed)> wrote in message
news:(E-Mail Removed)...
> 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"

John Salerno
Guest
Posts: n/a

 06-16-2008
"takayuki" <(E-Mail Removed)> wrote in message
news:(E-Mail Removed)...
> 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

MRAB
Guest
Posts: n/a

 06-16-2008
On Jun 16, 7:17 am, Paul Hankin <(E-Mail Removed)> wrote:
> On Jun 16, 2:35 pm, takayuki <(E-Mail Removed)> wrote:
>
>
>
> > 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

 Posting Rules You may not post new threads You may not post replies You may not post attachments You may not edit your posts BB code is On Smilies are On [IMG] code is On HTML code is OffTrackbacks are On Pingbacks are On Refbacks are Off Forum Rules

 Similar Threads Thread Thread Starter Forum Replies Last Post Isaac Won Python 9 03-04-2013 10:08 AM takayuki Python 2 06-16-2008 03:22 AM Michael W. Ryder Ruby 4 07-31-2007 08:29 AM Dominic Son Ruby 3 01-12-2007 12:19 PM addi ASP General 0 12-31-2003 04:31 AM