Enumerate question: Inner looping like in Perl

P

Pekka Niiranen

Hi,

I have Perl code looping thru lines in the file:

line: while (<INFILE>) {
...
$_ = do something
...
if (/#START/) {
# Start inner loop
while (<INFILE>) {
if (/#END/) {
next line;
}
}
}
if (/#BLAH1/) {
$_ = do something
}
if (/#BLAH2/) {
$_ = do something
}
}


I decided to use enumerate() in my Python version
since current line must be modified (the $_ = idiom in Perl)

---code starts---

fh = codecs.open(f_path, "rU", "utf-8")
contents = fh.readlines()
fh.close()

# Precompile regular expressions (speed hack?)
findSTART = r'#START'
matcherSTART = re.compile(findSTART, re.UNICODE)
findEND = r'#END'
matcherEND = re.compile(findEND, re.UNICODE)


for i, row in enumerate(contents):
row = something
if matcherSTART.search(row):
"Oops! how to advance 'i' and 'row' untill:
if matcherEND.search(row):
continue

---code ends---

I could create extra parameter to store the state of the loop like:

foundSTART = False
for i, row in enumerate(contents):
if foundSTART:
if not matcherEND.search(row):
continue
else:
foundSTART = False
else:
if matcherSTART.search(row):
foundSTART = True
if foundBLAH1:
row = something
if foundBLAH2:
row = something

but is this it really more maintainable code?

Is there enumerate() hack that I am missing or
should I go back to idiom?:

for i in range(len(contents)):
if matcherSTART.search(row):
while not matcherEND.search(row):
i = i + 1
continue


-pekka-
 
S

Steven Bethard

Pekka Niiranen said:
for i, row in enumerate(contents):
row = something
if matcherSTART.search(row):
"Oops! how to advance 'i' and 'row' untill:
if matcherEND.search(row):
continue


Usually I would do this kind of thing by just keeping the iterator around. The
example below has two loops, an outer and an inner, and both iterate over the
same enumeration. (I've used 'char ==' instead of regular expressions to make
the output a little clearer, but hopefully you can do the translation back to
regexps for your code.)
.... print 'outer', i, char
.... contents = char.upper()
.... if char == 'd':
.... for i, char in itr:
.... print 'inner', i, char
.... if char == 'f':
.... break
....
outer 0 a
outer 1 b
outer 2 c
outer 3 d
inner 4 e
inner 5 f
outer 6 g['A', 'B', 'C', 'D', 'e', 'f', 'G']

Steve
 
A

Alex Martelli

for i, row in enumerate(contents):
row = something
if matcherSTART.search(row):
"Oops! how to advance 'i' and 'row' untill:
if matcherEND.search(row):
continue


You take an explicit iterator and call its .next() method (or
equivalently use an inner for on the iterator, if that is sufficient, as
it appears to be in this case -- see later).
Is there enumerate() hack that I am missing or
should I go back to idiom?:

for i in range(len(contents)):
if matcherSTART.search(row):
while not matcherEND.search(row):
i = i + 1
continue


this 'idiom' won't do what you want; i is set again to the very next
value, ignoring all modifications in the loop's body, when execution
gets back to the loop's header.

Here, probably, is what you want:

looper = iter(enumerate(contents))
for i, row in looper:
row = something
if matcherSTART.search(row):
for i, row in looper:
if matcherEND.search(row):
break


Alex
 
A

Alex Martelli

Pekka Niiranen said:
... try:
... for i, char in itr:
... print 'inner', i, char
... if char == 'f':
... break
... except StopIteration:
... print "f not found" ...
Not working: Iter() takes care of its own exceptions?

Nope. Rather, the 'for' statement does not propagate the StopIteration
exception that terminates it -- such a propagation would be extremely
disruptive of 99% of Python's programs, and I'm astonished that you
expected it!

What you want is, I believe, among the Tutorial's examples...:

for i, ch in itr:
print 'inner', i, ch
if ch == 'f': break
else:
print 'f not found'

That's what the 'else' clause in a 'for' statement is for: it triggers
when the loop terminates normally (as opposed to ending with a break or
return or by propagating an exception). The ability to deal with a "not
found" case, where the "found" cases exit by break, is the main use case
of this 'else' clause.
This recurring feeling that writing REALLY correct programs in Python
is not easier than in lower level languages... :(

I think that you can get all the info about Python you need for the
purpose (and then some) from "Python in a Nutshell", but of course I'm
biased...;-)


Alex
 
B

Bengt Richter

Steven said:
for i, row in enumerate(contents):
row = something
if matcherSTART.search(row):
"Oops! how to advance 'i' and 'row' untill:
if matcherEND.search(row):
continue



Usually I would do this kind of thing by just keeping the iterator around. The
example below has two loops, an outer and an inner, and both iterate over the
same enumeration. (I've used 'char ==' instead of regular expressions to make
the output a little clearer, but hopefully you can do the translation back to
regexps for your code.)

contents = list('abcdefg')
itr = enumerate(contents)
for i, char in itr:

... print 'outer', i, char
... contents = char.upper()
... if char == 'd':
... for i, char in itr:
... print 'inner', i, char
... if char == 'f':
... break
...

Thanks, I decided to catch logical error of not
finding "f" -letter at all with:
... itr = enumerate(contents)
... for i, char in itr:
... print 'outer', i, char
... if char == 'd': #XXX# >... #try:
... for i, char in itr:
... print 'inner', i, char
... if char == 'f':
... break

else:
print "f not found"
#XXX >... #except StopIteration:
#XXX >... # print "f not found"
...outer 0 a
outer 1 b
outer 2 c
outer 3 d
inner 4 e
inner 5 g

Not working: Iter() takes care of its own exceptions?
This recurring feeling that writing REALLY correct programs in Python
is not easier than in lower level languages... :(

-pekka-




outer 0 a
outer 1 b
outer 2 c
outer 3 d
inner 4 e
inner 5 f
outer 6 g

['A', 'B', 'C', 'D', 'e', 'f', 'G']

Steve
Try above (untested), noting:
>>> itr = enumerate('abcdef')
>>> for i,c in itr:
... print i,c
... if c=='d': break
... else:
... print 'first else'
...
0 a
1 b
2 c
3 d ... print i,c
... if c=='z': break
... else:
... print '2nd else'
...
4 e
5 f
2nd else

Regards,
Bengt Richter
 
P

Pekka Niiranen

Steven said:
for i, row in enumerate(contents):
row = something
if matcherSTART.search(row):
"Oops! how to advance 'i' and 'row' untill:
if matcherEND.search(row):
continue



Usually I would do this kind of thing by just keeping the iterator around. The
example below has two loops, an outer and an inner, and both iterate over the
same enumeration. (I've used 'char ==' instead of regular expressions to make
the output a little clearer, but hopefully you can do the translation back to
regexps for your code.)


... print 'outer', i, char
... contents = char.upper()
... if char == 'd':
... for i, char in itr:
... print 'inner', i, char
... if char == 'f':
... break
...

Thanks, I decided to catch logical error of not
finding "f" -letter at all with:
.... itr = enumerate(contents)
.... for i, char in itr:
.... print 'outer', i, char
.... if char == 'd':
.... try:
.... for i, char in itr:
.... print 'inner', i, char
.... if char == 'f':
.... break
.... except StopIteration:
.... print "f not found"
....outer 0 a
outer 1 b
outer 2 c
outer 3 d
inner 4 e
inner 5 g

Not working: Iter() takes care of its own exceptions?
This recurring feeling that writing REALLY correct programs in Python
is not easier than in lower level languages... :(

-pekka-




outer 0 a
outer 1 b
outer 2 c
outer 3 d
inner 4 e
inner 5 f
outer 6 g

['A', 'B', 'C', 'D', 'e', 'f', 'G']

Steve
 

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

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,901
Latest member
Noble71S45

Latest Threads

Top