newbie question: for loop within for loop confusion

Discussion in 'Python' started by takayuki, Jun 16, 2008.

  1. takayuki

    takayuki Guest

    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
     
    takayuki, Jun 16, 2008
    #1
    1. Advertising

  2. takayuki

    Roy Smith Guest

    In article
    <>,
    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.
     
    Roy Smith, Jun 16, 2008
    #2
    1. Advertising

  3. takayuki

    TheSaint Guest

    TheSaint, Jun 16, 2008
    #3
  4. takayuki

    John Salerno Guest

    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, Jun 16, 2008
    #4
  5. takayuki

    John Salerno Guest

    takayuki wrote:

    > inchworm
    > inchworm


    P.S. Why does 'inchworm' only print twice? Or is that not the full output?
     
    John Salerno, Jun 16, 2008
    #5
  6. takayuki

    takayuki Guest

    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')
     
    takayuki, Jun 16, 2008
    #6
  7. takayuki

    Paul Hankin Guest

    On Jun 16, 2:35 pm, takayuki <> 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
     
    Paul Hankin, Jun 16, 2008
    #7
  8. takayuki

    John Salerno Guest

    "takayuki" <> wrote in message
    news:...
    > 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, Jun 16, 2008
    #8
  9. takayuki

    John Salerno Guest

    "takayuki" <> wrote in message
    news:...
    > 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
     
    John Salerno, Jun 16, 2008
    #9
  10. takayuki

    MRAB Guest

    On Jun 16, 7:17 am, Paul Hankin <> wrote:
    > On Jun 16, 2:35 pm, takayuki <> 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.)
     
    MRAB, Jun 16, 2008
    #10
  11. takayuki

    Thomas Hill Guest

    On Jun 15, 6:23 pm, takayuki <> wrote:
    > 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 Hill, Jun 16, 2008
    #11
  12. takayuki

    Thomas Hill Guest

    On Jun 16, 2:34 pm, Thomas Hill <> wrote:
    > On Jun 15, 6:23 pm, takayuki <> wrote:
    >
    > > 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?
     
    Thomas Hill, Jun 16, 2008
    #12
  13. takayuki

    takayuki Guest

    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, Jun 17, 2008
    #13
  14. takayuki

    takayuki Guest

    On Jun 17, 6:34 am, Thomas Hill <> wrote:
    > On Jun 15, 6:23 pm, takayuki <> wrote:
    >
    > > 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
     
    takayuki, Jun 17, 2008
    #14
  15. takayuki

    John Salerno Guest

    takayuki wrote:

    > 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.
     
    John Salerno, Jun 17, 2008
    #15
  16. takayuki

    David Guest

    takayuki wrote:
    > 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
    > --
    > http://mail.python.org/mailman/listinfo/python-list
    >
    >
    >


    takayuki,

    Could you post the complete script.
    thanks
    david


    --
    Powered by Gentoo GNU/LINUX
    http://www.linuxcrazy.com
     
    David, Jun 17, 2008
    #16
  17. En Mon, 16 Jun 2008 22:51:30 -0300, John Salerno <> escribió:

    > takayuki wrote:
    >
    >> 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.


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

    --
    Gabriel Genellina
     
    Gabriel Genellina, Jun 17, 2008
    #17
  18. takayuki

    John Salerno Guest

    Gabriel Genellina wrote:
    > En Mon, 16 Jun 2008 22:51:30 -0300, John Salerno <> escribió:
    >
    >> takayuki wrote:
    >>
    >>> 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.

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


    I know, but I just hear so many people recommend that book for people
    who want to learn the language.
     
    John Salerno, Jun 17, 2008
    #18
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. takayuki
    Replies:
    2
    Views:
    291
    Calvin Spealman
    Jun 16, 2008
  2. addi
    Replies:
    0
    Views:
    292
  3. Dominic Son

    A loop within another loop..

    Dominic Son, Jan 11, 2007, in forum: Ruby
    Replies:
    3
    Views:
    132
    Snow Man
    Jan 12, 2007
  4. Michael Randall

    CSV confusion newbie question

    Michael Randall, Dec 6, 2009, in forum: Ruby
    Replies:
    1
    Views:
    97
    Michael R.
    Dec 6, 2009
  5. Isaac Won
    Replies:
    9
    Views:
    448
    Ulrich Eckhardt
    Mar 4, 2013
Loading...

Share This Page