Skip to content

Instantly share code, notes, and snippets.

@hellertime
Created November 12, 2010 19:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hellertime/674538 to your computer and use it in GitHub Desktop.
Save hellertime/674538 to your computer and use it in GitHub Desktop.
Group a sorted list into tuples with each tuple identifying a consecutive range in the list
import sys
# requires Python 2.5
assert sys.version_info[1] >= 5, "Requires Python Coroutines. See PEP 0342"
def pushbackiter(iterator):
"""converts 'iterator' into a coroutine, with limited pushback
>>> it = pushbackiter([1,2,3,4,5])
>>> it.next()
1
>>> it.send(1)
None
>>> it.next()
1
"""
for elem in iterator:
pushback = yield elem # yield 'elem' to the caller of '.next()', 'pushback' will become == 'None'
if pushback is not None: # this implementation is unable to take back a None value
yield None # yield 'None' to the caller of '.send(x)'
pushback = yield pushback # sets 'pushback' to the value passed in via '.send(x)'
def intervals(xs):
"""groups 'xs' into a pair generator indicating consecutive ranges
>>> list(intervals([1,2,3,5,7,8,12,15,16])
[(1,3),(5,),(7,8),(12,),(15,16)]
"""
def peek(xs):
"""helper function does the pushback for us"""
x = xs.next()
xs.send(x)
return x
xs = pushbackiter(xs)
for x in xs:
x_ = x
while True: # locate the last successor of x
try:
y = peek(xs)
if x_ + 1 == y:
x_ = y
y = xs.next()
continue
elif x_ == x: # x_ + 1 is not a successor and we have not iterated off the left bound
x_ = None
except StopIteration:
x_ = None
break
if x_ is not None:
yield (x, x_)
else:
yield (x,)
@hellertime
Copy link
Author

See gist https://gist.github.com/674679 for a version of this in Haskell

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment