Most of the times it is easier (and cheaper) to make the first iteration the special case instead of the last one.
Most of the times it is easier (and cheaper) to make the first iteration the special case instead of the last one: first = True for data in data_list: if first: first = False else: between_items() item() This will work for any iterable, even for those that have no len(): file = open('/path/to/file') for line in file: process_line(line) # No way of telling if this is the last line! Apart from that, I don't think there is a generally superior solution as it depends on what you are trying to do. For example, if you are building a string from a list, it's naturally better to use str.join() than using a for loop “with special case�.
Using the same principle but more compact: for i, line in enumerate(data_list): if I > 0: between_items() item() Looks familiar, doesn't it? :).
True, this way seems better than mine, at least it don't need to use enumerate and len. – e. Tadeu Oct 27 '09 at 12:11 Yes, but it adds another if which could be avoided if the loop was split into two loops.
However, this is relevant only when iterating a huge data list. – Adam Matan Oct 27 '09 at 12:13 The problem with splitting into two loops is that it either violates DRY or it forces you to define methods. – e.
Tadeu Oct 27 '09 at 12:15 I liked your way to do it, only if it were more compact... ;) – e. Tadeu Oct 27 '09 at 12:24.
The 'code between' is an example of the Head-Tail pattern. You have an item, which is followed by a sequence of ( between, item ) pairs. You can also view this as a sequence of (item, between) pairs followed by an item.It's generally simpler to take the first element as special and all the others as the "standard" case.
Further, to avoid repeating code, you have to provide a function or other object to contain the code you don't want to repeat. Embedding an if statement in a loop which is always false except one time is kind of silly. Def item_processing( item ): # *the common processing* head_tail_iter = iter( someSequence ) head = head_tail_iter.next() item_processing( head ) for item in head_tail_iter: # *the between processing* item_processing( item ) This is more reliable because it's slightly easier to prove, It doesn't create an extra data structure (i.e.
, a copy of a list) and doesn't require a lot of wasted execution of an if condition which is always false except once.
1 Function calls are way slower then if statements so the “wasted execution†argument does not hold. – Ferdinand Beyer Nov 4 '09 at 18:41 I'm not sure what the speed difference between function call and if-statement has to do with anything. The point is that this formulation has no if-statement that's always false (except once.) – S.
Lott Nov 4 '09 at 18:43 I interpreted your statement “…and doesn't require a lot of wasted execution of an if condition which is always false except once†as “…and is faster since it saves a couple of ifsâ€. Obviously you are just refering to “code cleanlinessâ€? – Ferdinand Beyer Nov 4 '09 at 18:52.
If you're simply looking to modify the last element in data_list then you can simply use the notation: L-1 However, it looks like you're doing more than that. There is nothing really wrong with your way. I even took a quick glance at some Django code for their template tags and they do basically what you're doing.
I'm not modifying it, I'm using it to do something – e. Tadeu Oct 27 '09 at 12:20.
This is similar to Ants Aasma's approach but without using the itertools module. It's also a lagging iterator which looks-ahead a single element in the iterator stream: def last_iter(it): # Ensure it's an iterator and get the first field it = iter(it) prev = next(it) for item in it: # Lag by one item so I know I'm not at the end yield 0, prev prev = item # Last item yield 1, prev def test(data): result = list(last_iter(data)) if not result: return if len(result) > 1: assert set(x0 for x in result:-1) == set(0), result assert result-10 == 1 test() test(1) test(1, 2) test(range(5)) test(xrange(4)) for is_last, item in last_iter(""): print is_last, item.
I liked this solution too! – e. Tadeu Oct 28 '09 at 11:58.
After all, a loop is created to do something similar to all elements you loop over; if one element needs something special, it shouldn't be in the loop. (see also this question: does-the-last-element-in-a-loop-deserve-a-separate-treatment) EDIT: since the question is more about the "in between", either the first element is the special one in that it has no predecessor, or the last element is special in that it has no successor.
But the last element should be treated similar to every other element in the list. The problem is the thing that should be done only between elements. – e.
Tadeu Oct 27 '09 at 12:16 In that case, the first one is the only one without a predecessor. Take that one apart, and loop over the rest of the list general code. – xtofl Oct 27 '09 at 12:39.
You can use a sliding window over the input data to get a peek at the next value and use a sentinel to detect the last value. This works on any iterable, so you don't need to know the length beforehand. The pairwise implementation is from itertools recipes.
From itertools import tee, izip, chain def pairwise(seq): a,b = tee(seq) next(b, None) return izip(a,b) def annotated_last(seq): """Returns an iterable of pairs of input item and a boolean that show if the current item is the last item in the sequence. """ MISSING = object() for current_item, next_item in pairwise(chain(seq, MISSING)): yield current_item, next_item is MISSING: for item, is_last_item in annotated_last(data_list): if is_last_item: # current item is the last item.
Assuming input as an iterator, here's a way using tee and izip from itertools: from itertools import tee, izip items, between = tee(input_iterator, 2) # Input must be an iterator. First = items.next() do_to_every_item(first) # All "do to every" operations done to first item go here. For i, be in izip(items, between): do_between_items(b) # All "between" operations go here.
Do_to_every_item(i) # All "do to every" operations go here. Demo: >>> def do_every(x): print "E", x ... >>> def do_between(x): print "B", x ... >>> test_input = iter(range(5)) >>> >>> from itertools import tee, izip >>> >>> items, between = tee(test_input, 2) >>> first = items.next() >>> do_every(first) E 0 >>> for i,b in izip(items, between): ... do_between(b) ... do_every(i) ... B 0 E 1 B 1 E 2 B 2 E 3 B 3 E 4.
There is nothing wrong with your way, unless you will have 100 000 loops and wants save 100 000 "if" statements. In that case, you can go that way : iterable = 1,2,3 # Your date iterator = iter(iterable) # get the data iterator try : # wrap all in a try / except while 1 : item = iterator.next() print item # put the "for loop" code here except StopIteration, e : # make the process on the last element here print item Outputs : 1 2 3 3 But really, in your case I feel like it's overkill. In any case, you will probably be luckier with slicing : for item in iterable:-1 : print item print "last :", iterable-1 #outputs 1 2 last : 3 or just : for item in iterable : print item print iterable-1 #outputs 1 2 3 last : 3 Eventually, a KISS way to do you stuff, and that would work with any iterable, including the ones without __len__ : item = '' for item in iterable : print item print item Ouputs: 1 2 3 3 If feel like I would do it that way, seems simple to me.
But note that iterable-1 will not work to all iterables (such as generator that do not have len) – e. Tadeu Oct 27 '09 at 12:23 Right, I edited it to add the hack I would use myself. Not the brightest, but definitly the easiest.
– e-satis Oct 27 '09 at 12:25 If all you want is to access the last item after the loop, simply use item instead of re-calculating it using list-1. But nevertheless: I don't think this is what the OP was asking for, was it? – Ferdinand Beyer Oct 27 '09 at 12:27 Compensated the -1, it was not said in the problem definition that len was to be avoided (even used in the example).
– RedGlyph Oct 27 '09 at 12:35 Maybe I didn't get the question then. – e-satis Oct 27 '09 at 12:35.
I cant really gove you an answer,but what I can give you is a way to a solution, that is you have to find the anglde that you relate to or peaks your interest. A good paper is one that people get drawn into because it reaches them ln some way.As for me WW11 to me, I think of the holocaust and the effect it had on the survivors, their families and those who stood by and did nothing until it was too late.