I\'m looking for a way to easily determine if all not None items in a list occur in a single continuous slice. I\'ll use integers as examples of not None items
My first approach was to use variables to keep track ...
...this ends up with a highly nested and very difficult to follow series of if/else statements embedded in a for loop...
No! Actually you need only one variable. Thinking this problem in the view of Finite State Machine(FSM) with your approach will lead to a quite nice solution.
We call the state p
. At first, p
is 0. Then we start walking between the states.
When all the elements in the list is examinated and still don't fail then the answer is True
.
One version that encode the translation table in a dict
def contiguous(s, _D={(0,0):0, (0,1):1, (1,0):2, (1,1):1, (2,0):2, (2,1):3}):
p = 0
for x in s:
p = _D[p, int(x is not None)]
if p >= 3: return False
return True
Another version that use if statement:
def contiguous(s):
p = 0
for x in s:
if x is None and p == 1 or x is not None and (p == 0 or p == 2):
p += 1
if p >= 3: return False
return True
So my point is that using if
and for
are still pythonic.
I found another way to encode the FSM. We can pack the translation table into a 12bit integer.
def contiguous(s):
p = 0
for x in s:
p = (3684 >> (4*p + 2*(x!=None))) & 3
if p >= 3: return False
return True
Here 3684, the magic number, can be obtained by:
_D[p,a] 3 2 1 2 1 0
p 2 2 1 1 0 0
a 1 0 1 0 1 0
bin(3684) = 0b 11 10 01 10 01 00
The readability is not as good as other version but it's faster since it avoids dictionary lookup. The second version is as fast as this but this encoding idea can be generalized to solve more problems.