I often have the case where I need to loop through a bunch of elements, but
do something special on for the last line of code.
Currently, I can solve it this way:
first = 1
for port in self.portDefsList:
if first == 0:
string += ", "
else:
first = 0
string += str(port)
pass
This works fine, but it introduces an extra variable. Is there a cleaner
way to do this?
Thanks,
Tom
>
> Hello All,
>
> I often have the case where I need to loop through a bunch of elements,
> but do something special on for the last line of code.
That's not what you're doing below:
> Currently, I can solve it this way:
>
> first = 1
> for port in self.portDefsList:
> if first == 0:
> string += ", "
> else:
> first = 0
> string += str(port)
> pass
Here, you're doing something special (avoiding the += ", ") the FIRST
time. The pass is unconditional and adds absolutely nothing -- just
take it away.
Anyway, this specific need is met to perfection by:
string += ", ".join([ str(port) for port in self.portDefsList ])
joiner.join(sequenceOfStrings) is a powerful way to meet many
problems of the "I must do something each time except the first/last"
variety -- all those problems which boil down to joining strings
with some joiner.
As a nice plus, it's also *WAY, WAY* faster. A loop with += takes
O(N squared) time, joiner.join takes O(N) time, where N is the
size of the input. Don't use loops of += on strings except in
really short, trivial cases -- prepare a list and join it at the
end, or use a cStringIO instance x and call x.write, etc, etc.
For more general needs, you can sometimes use slices to good effect.
I.e., instead of:
first = 1
for item in somelist:
if first:
processfirst(item)
first = 0
else:
processnormal(item)
you can do:
processfirst(somelist[0])
for item in somelist[1:]:
processnormal(item)
and if the special case needs to be the LAST one:
for item in somelist[:-1]:
processnormal(item)
processlast(somelist[-1])
This does assume that you have a sequence, not just an iterator, and can
thus slice off the first or last items easily. It's trickier but
not difficult to do special-on-first on an iterator:
processfirst(iterator.next())
for item in iterator:
processnormal(item)
the .next call returns and "consumes" the first item, so the following
for starts from the second one, and all is sweetness and light.
Detecting the LAST item for special processing is trickier still if
what you DO have is an iterator. You basically have to "stagger"
the output, e.g.:
saved = iterator.next()
for item in iterator:
processnormal(saved)
saved = item
processlast(saved)
There's no way out here from the "extra variable" -- you may hide
it somewhere, but SOMEwhere it does have to be.
Alex
> first = 1
> for port in self.portDefsList:
> if first == 0:
> string += ", "
> else:
> first = 0
> string += str(port)
> pass
You actually special case the first item. However, you really want
the string join method:
result = ', '.join(map(str, self.portDefsList))
Terry J. Reedy
>>
>> Hello All,
>>
>> I often have the case where I need to loop through a bunch of elements,
>> but do something special on for the last line of code.
>
> That's not what you're doing below:
>
>> Currently, I can solve it this way:
>>
>> first = 1
>> for port in self.portDefsList:
>> if first == 0:
>> string += ", "
>> else:
>> first = 0
>> string += str(port)
>> pass
>
> Here, you're doing something special (avoiding the += ", ") the FIRST
> time. The pass is unconditional and adds absolutely nothing -- just
> take it away.
Duh. I copied the wrong piece of code. :-]
> Anyway, this specific need is met to perfection by:
> string += ", ".join([ str(port) for port in self.portDefsList ])
Interesting.
My focus wasn't really on joining strings, but I didn't know this either.
> This does assume that you have a sequence, not just an iterator, and can
> thus slice off the first or last items easily. It's trickier but
> not difficult to do special-on-first on an iterator:
>
> processfirst(iterator.next())
> for item in iterator:
> processnormal(item)
>
> the .next call returns and "consumes" the first item, so the following
> for starts from the second one, and all is sweetness and light.
I am not familiar with iterators as objects in Python. I understand that
for a in myList:
...
will result in an implicit iterator on myList. How would you create an
explicit iterator object of myList?
> Detecting the LAST item for special processing is trickier still if
> what you DO have is an iterator. You basically have to "stagger"
> the output, e.g.:
>
> saved = iterator.next()
> for item in iterator:
> processnormal(saved)
> saved = item
> processlast(saved)
Given that you have an explicit iterator, wouldn't it be trivial to add an
'end()' method to this iterator to indicate the end of the sequence (just
like C++ iterators) ?
This would result in:
for item in iterator:
if iterator.next().end():
do_something_special
do_something_for_all
Thanks,
Tom
> I am not familiar with iterators as objects in Python. I understand that
>
> for a in myList:
> ...
>
> will result in an implicit iterator on myList. How would you create an
> explicit iterator object of myList?
the built-in iter() function will do this for you:
>>> L = [1, 2, 3, 4, 5]
>>> I = iter(L)
>>> L
[1, 2, 3, 4, 5]
>>> I
<iterator object at 0x007D9728>
>>> I.next()
1
>>> for i in I:
... print i
...
2
3
4
5
>>> I.next()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
StopIteration
</F>
For a simple solution, how about:
for a in myList[:-1]:
do_stuff(a)
special_stuff(myList[-1])
[...]
> Given that you have an explicit iterator, wouldn't it be trivial to
add an
> 'end()' method to this iterator to indicate the end of the sequence
(just
> like C++ iterators) ?
>
> This would result in:
>
> for item in iterator:
> if iterator.next().end():
> do_something_special
>
> do_something_for_all
I think you've misunderstood the end() value in C++ STL iterators. It
is not the last item, but one past the last item. The iterator can take
the end value, but dereferencing the end value is illegal.
--Bryan
> For a simple solution, how about:
>
> for a in myList[:-1]:
> do_stuff(a)
> special_stuff(myList[-1])
No, I still want to do 'do_stuff' for the last element also. This may be,
say, 10 lines of code. Too much to duplicate it outside the loop, not
enough for a separate function...
> > Given that you have an explicit iterator, wouldn't it be trivial to
> add an
> > 'end()' method to this iterator to indicate the end of the sequence
> (just
> > like C++ iterators) ?
> >
> > This would result in:
> >
> > for item in iterator:
> > if iterator.next().end():
> > do_something_special
> >
> > do_something_for_all
>
> I think you've misunderstood the end() value in C++ STL iterators. It
> is not the last item, but one past the last item. The iterator can take
> the end value, but dereferencing the end value is illegal.
That would be the case if I would check for iterator.end(), but I check for
iterator.next().end() !
Tom
> > For a simple solution, how about:
> >
> > for a in myList[:-1]:
> > do_stuff(a)
> > special_stuff(myList[-1])
> No, I still want to do 'do_stuff' for the last element also. This may be,
> say, 10 lines of code. Too much to duplicate it outside the loop, not
> enough for a separate function...
I did not follow this thread, I may be missing something, but why not:
for a in myList:
do_stuff(a)
special_stuff(a)
taking advantage on the fact that `a' keeps the last value it received?
--
François Pinard http://www.iro.umontreal.ca/~pinard
> > For a simple solution, how about:
> >
> > for a in myList[:-1]:
> > do_stuff(a)
> > special_stuff(myList[-1])
>
> No, I still want to do 'do_stuff' for the last element also. This may be,
> say, 10 lines of code. Too much to duplicate it outside the loop, not
> enough for a separate function...
so why not just change the for loop?
for a in myList:
do_stuff(a)
special_stuff(myList[-1])
for extra style points, you can use the else statement to
make sure the special stuff isn't done if you have to break
out of the loop:
for a in myList:
do_stuff(a)
else:
special_stuff(a)
> That would be the case if I would check for iterator.end(), but I check for
> iterator.next().end() !
as the code in my earlier post tried to tell you, iterator.next()
returns the next item from the sequence, or raises an exception.
to implement your proposal, *all* objects need to implement an
end method, which should return true if they are the last object
in a container. since objects can be shared (the containers only
hold references), and objects don't know or care about what
containers they are in today, that's pretty much means that
we have to start over from scratch...
</F>
<!-- (the eff-bot guide to) the python standard library:
http://www.pythonware.com/people/fredrik/librarybook.htm
-->
Why is 10 lines of code not enough for a separate function? I use
functions to group code into conceptual blocks that can be thought about
and understood as a unit. Sometimes that block is 100 lines of code,
sometimes it's 3 or 4 lines. I've even written one-line functions.
Whatever makes sense to factor out as an atomic unit. Line count has
little to do with it.
I'm not quite as rabid about it as the XP'er are with "Refactor
Mercilessly", but (like so much with XP) the basic idea has merit if you
don't try to be too extreme about it :-)
Tom> Hello All,
Tom> I often have the case where I need to loop through a bunch of
Tom> elements, but do something special on for the last line of
Tom> code.
What about the good, old fashioned index? To my eyes, it is very
readable
N = len(seq):
for i in range(N):
do_something(seq[i])
if i==someVal:
do_something_else(seq[i])
It's not really that god awful is it?
Also, for the example you posted, you can use string.join....
import string
seq = range(10)
s = string.join(map(str, seq), ', ')
Cheers,
John Hunter
> I did not follow this thread, I may be missing something, but why not:
>
> for a in myList:
> do_stuff(a)
> special_stuff(a)
>
> taking advantage on the fact that `a' keeps the last value it received?
Then what about this?
for a in myList:
if last_one:
special_stuff
do_stuff
Tom
> Tom Verbeure <tom.ve...@verizon.no.sp.am.net> wrote:
>> say, 10 lines of code. Too much to duplicate it outside the loop, not
>> enough for a separate function...
>
> Why is 10 lines of code not enough for a separate function? I use
> functions to group code into conceptual blocks that can be thought about
> and understood as a unit. Sometimes that block is 100 lines of code,
> sometimes it's 3 or 4 lines. I've even written one-line functions.
> Whatever makes sense to factor out as an atomic unit. Line count has
> little to do with it.
Ha! Now we are talking style! :-)
Yes, that's, of course, an option also, but we are moving away from the
original question and I feel guilty already wasting this forums' reader
time with my trival question...
My main reason not to use function in this case, is that it may result in
moving meaning full code too far away from where the action is. However,
after reading some things on the web, I can solve this by added nested
functions:
def BigFunction:
< lots of code >
def DefaultForLoopCode():
pass
for a in myList[:-1]:
DefaultForLoopCode()
DoExtra()
DefaultForLoopCode()
I think this will solve my problem, while still keeping everything
relatively clean.
Thanks,
Tom
> Then what about this?
> for a in myList:
> if last_one:
> special_stuff
> do_stuff
Eh! Some require a bit more work than others :-).
Now this comment I find strange, as personally I consider 10 lines of
code about the perfect length for a function. If your functions are
consise they become self documenting. As an added bonus, similar
functions appearing in different sections (often unrelated) of code,
can often suggest powerful abstractions that can make your code more
flexible and more maintainable. I personally prefer a function to do
one thing. If I find myself needing more then 15-20 lines to describe
'one thing' I stop and ask myself why I'm having so much trouble
describing 'one thing' consisely. Is the function actually doing
multiple things? Do I really understand what the functions trying to
do? Am I trying to use the wrong programmign-paradigm to describe
'one thing'? Maybe the function's in the wrong place, and working too
hard to obtain the data it needs? Whatever the problem, I find a
function of >20 lines a symptom that there is something wrong.
5-10 lines + error handling.
that's-my-general-target-ly yours
Andrae Muys
> Then what about this?
>
> for a in myList:
> if last_one:
> special_stuff
> do_stuff
> Tom
Okay, not as simple, but it should work
for a, i in zip(myList, xrange(len(myList))):
if i==(len(myList)-1):
special_stuff
do_stuff
--
Christopher A. Craig <list-...@ccraig.org>
"Imagination is more important than knowledge" Albert Einstein
> Given that you have an explicit iterator, wouldn't it be trivial to add an
> 'end()' method to this iterator to indicate the end of the sequence (just
> like C++ iterators) ?
>
> This would result in:
>
> for item in iterator:
> if iterator.next().end():
> do_something_special
>
> do_something_for_all
I do not think it means what you think it means:
>>> iterator = iter([0,1,2,3])
>>> for i in iterator:
... if iterator.next(): pass
... print i
0
2
As you see, iterator.next() moves iterator to the next item, which
means that even if you had a .end() method (which you don't), you
couldn't use it like this.
One way you could do this (another, better, way to do it is in another
post, but only works if you know the length at the beginning) is to
always track the next item:
next = iterator.next()
while next:
this=next
try:
next=iterator.next()
except StopIteration:
next=False
do_something_special
do_something_for_all
--
Christopher A. Craig <list-...@ccraig.org>
"640K ought to be enough for anybody." Bill Gates
For a given C++ STL iterator, end() will return the *same* value no
matter how many times you advance the iterator. It looks like you
wanted:
...
if iterator.next() == iterator.end():
do_something_special
--Bryan
On Sat, 27 Jul 2002 20:42:03 GMT, Alex Martelli <al...@aleax.it>
wrote:
>As a nice plus, it's also *WAY, WAY* faster. A loop with += takes
>O(N squared) time, joiner.join takes O(N) time, where N is the
>size of the input. Don't use loops of += on strings except in
>really short, trivial cases -- prepare a list and join it at the
>end, or use a cStringIO instance x and call x.write, etc, etc.
I am using the following code structure in some very long (10's of
thousands of iterations) loops.
Is there a more efficient way that doesn't use +=?
Would s5 = '%s,"%s"' % (s5,x[1]) be better?
t = ()
s5 = ''
for i in range(0,5):
q = breaks[brk][i]
x = mkup(cost*q,list*q)
if x[0]: t+=(x[0]/q,)
else: t+=(0,)
if x[1]: s5+=',"'+x[1]+'"'
else: s5+=',"-"'
t += (0,0)
s5 += ',"-","-"'
Bill
[snip]
> I am using the following code structure in some very long (10's of
> thousands of iterations) loops.
>
> Is there a more efficient way that doesn't use +=?
>
> Would s5 = '%s,"%s"' % (s5,x[1]) be better?
Probably not (it would still have the O(N squared) charactaristics).
Instead try:
s5_list = []
for ...:
s5_list.append(',"%s"' % x[1])
s5 = ''.join(s5_list)
--
Best Regards
Thomas Jensen
(remove underscore in email address to mail me)
> Would s5 = '%s,"%s"' % (s5,x[1]) be better?
>
> t = ()
> s5 = ''
> for i in range(0,5):
> q = breaks[brk][i]
> x = mkup(cost*q,list*q)
> if x[0]: t+=(x[0]/q,)
> else: t+=(0,)
> if x[1]: s5+=',"'+x[1]+'"'
> else: s5+=',"-"'
> t += (0,0)
> s5 += ',"-","-"'
Without exactly understanding what it does, here are some tips:
1) make t a list; this allows you to use 'append'. This is much faster since
append works on an existing list. <tuple> += <tuple> creates a new tuple
every time. <list> += <list> is quite efficient as well
2) make s a list as well, append all the strings you need, and join them in
the end.
Something like this should be faster:
t_list = []
s5_list = []
for i in range(0, 5):
q = breaks[break][i]
x = mkup(cost * q, list * q)
if x[0]:
t_list.append(x[0]/q)
else:
t_list.append(0)
if x[1]:
s5_list += [ ',"' , x[1] , '"']
else:
s5_list.append(',"-"')
t_list += (0, 0)
s5_list.append(',"-","-"')
# and convert both lists to the desired types
t = tuple(t_list)
s5 = "".join(s5_list)
No clue how much faster; that's what profile is for.
YDD
--
.sigmentation fault
That works, because slicing an empty list always returns an empty list,
while accessing an nonexistant element throws an exception. :)
Andreas
Andreas
> Something like this should be faster:
>
> t_list = []
> s5_list = []
> for i in range(0, 5):
> q = breaks[break][i]
> x = mkup(cost * q, list * q)
> if x[0]:
> t_list.append(x[0]/q)
> else:
> t_list.append(0)
>
> if x[1]:
> s5_list += [ ',"' , x[1] , '"']
> else:
> s5_list.append(',"-"')
> t_list += (0, 0)
> s5_list.append(',"-","-"')
>
> # and convert both lists to the desired types
> t = tuple(t_list)
> s5 = "".join(s5_list)
>
>
> No clue how much faster; that's what profile is for.
A few other suggestions that might or might not help:
I'm assuming the real code is inside a function, if not put it there at
once (that will speed it up a lot).
If mkup is simply returning two values, then use tuple unpacking to assign
the results directly into two variables instead of indexing. This allows
you to give the results meaningful names.
If the first result of mkup is always numeric, then you can collapse the if
statement down to a single append.
Optimise out the references to the append method from inside the loop.
Avoid using range in a for loop. 'i' isn't needed here at all.
All of which together (apart from the function) give you something like:
t_list, s5_list = [], []
t_append, s_append = t_list.append, s5_list.append
for q in breaks[break]:
numeric, stringval = mkup(cost * q, list * q)
t_append(numeric and numeric/q)
s_append(',"%s"' % stringval or "-")
t_list += (0, 0)
s_append(',"-","-"')
# and convert both lists to the desired types
t = tuple(t_list)
s5 = "".join(s5_list)
--
Duncan Booth dun...@rcp.co.uk
int month(char *p){return(124864/((p[0]+p[1]-p[2]&0x1f)+1)%12)["\5\x8\3"
"\6\7\xb\1\x9\xa\2\0\4"];} // Who said my code was obscure?