try: last_found = -1 for num in L1: last_found = L2.index(num, last_found + 1) return True except ValueError: return False
The index method of list L2 returns the position at which the first argument ( num ) was found in the list; called, as here, with a second argument, he begins to search the list at that position. If index does not find what it is looking for, it throws a ValueError exception.
So, this code uses this approach to search for each element num of L1 , in order, inside L2 . The first time he needs to start looking from position 0; each subsequent point in time, he should begin to look from the position immediately after the last one, where he found the previous element, i.e. last_found + 1 (so in the beginning we should set last_found = -1 to start looking at position 0 for the first time).
If each element in L1 is found in this way (i.e., it was found in L2 after the position where the previous element was found), then the two lists satisfy the given condition, and the code returns True . If any L1 element has never been found, the code catches the resulting ValueError exception and simply returns False .
Another approach would be to use iterators over two lists, which can be formed using the built-in iter function. You can "advance" the iterator by invoking the built-in next on it; this will increase StopIteration if there is no "next element", that is, the iterator is exhausted. You can also use for on an iterator for a smoother interface where applicable. Low level approach using iter / next idea:
i1 = iter(L1) i2 = iter(L2) while True: try: lookfor = next(i1) except StopIteration: # no more items to look for == all good! return True while True: try: maybe = next(i2) except StopIteration: # item lookfor never matched == nope! return False if maybe == lookfor: break
or, a slightly higher level:
i1 = iter(L1) i2 = iter(L2) for lookfor in i1: for maybe in i2: if maybe == lookfor: break else:
In fact, the only key use of iter here is to get i2 - having an inner loop like for maybe in i2 ensures that the inner loop will not start looking from the very beginning every time, but rather, it will keep looking where it last time. The outer loop can also be used for for lookfor in L1: since it does not have a reboot problem.
The key here is an else: loop else: , which starts if and only if the loop was not interrupted by a break , but rather a natural one.
While working on this idea, we were again reminded of the in operator, which could also be continued when it was last left with just an iterator. Great simplification:
i2 = iter(L2) for lookfor in L1: if lookfor not in i2: return False
But now we understand that this is exactly what abstracts with the short-circuited any and all built-in functions of the "short circuit", therefore ...:
i2 = iter(L2) return all(lookfor in i2 for lookfor in L1)
which, I believe, is almost as simple as you can get. The only non-elementary bit left here: you need to explicitly use iter(L2) , only once to make sure that the in operator (internally closed loop) does not restart the search from the very beginning, but rather continues every time from which it expired.