Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Python > newbie question: for loop within for loop confusion

Reply
Thread Tools

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
 
Reply With Quote
 
 
 
 
Roy Smith
Guest
Posts: n/a
 
      06-16-2008
In article
<a0ff2569-a1a9-44c3-a68a->,
takayuki <> 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...

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.
 
Reply With Quote
 
 
 
 
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
 
Reply With Quote
 
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.
 
Reply With Quote
 
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?
 
Reply With Quote
 
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')


 
Reply With Quote
 
Paul Hankin
Guest
Posts: n/a
 
      06-16-2008
On Jun 16, 2:35*pm, takayuki <lawtonp...@gmail.com> 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
 
Reply With Quote
 
John Salerno
Guest
Posts: n/a
 
      06-16-2008
"takayuki" <> wrote in message
news:ba3ce6db-6d2b-4de4-b1d2-...
> 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"


 
Reply With Quote
 
John Salerno
Guest
Posts: n/a
 
      06-16-2008
"takayuki" <> wrote in message
news:a0ff2569-a1a9-44c3-a68a-...
> 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


 
Reply With Quote
 
MRAB
Guest
Posts: n/a
 
      06-16-2008
On Jun 16, 7:17 am, Paul Hankin <paul.han...@gmail.com> wrote:
> On Jun 16, 2:35 pm, takayuki <lawtonp...@gmail.com> 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

(parentheses added for clarity.)
 
Reply With Quote
 
 
 
Reply

Thread Tools

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 Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Triple nested loop python (While loop insde of for loop inside ofwhile loop) Isaac Won Python 9 03-04-2013 10:08 AM
newbie: for loop within for loop question takayuki Python 2 06-16-2008 03:22 AM
Confusion with while loop Michael W. Ryder Ruby 4 07-31-2007 08:29 AM
A loop within another loop.. Dominic Son Ruby 3 01-12-2007 12:19 PM
help: loop within a loop -how to display data in 3 columns by 4 rows format addi ASP General 0 12-31-2003 04:31 AM



Advertisments
 



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57