
# A generator for 0, 1, 2, ..., n

def lazy_range(n):
    """a lazy version of range"""
    i = 0
    while i < n:
        yield i
        i += 1

# The following loop will consume the yielded values one at a time 
# until none are left:

for i in lazy_range(10):
   print(i)

# NOTE: in Python 3, range(n) itself is lazy !!!

print()

# ###########################################################
# You can create "infinite" iterators:
# ###########################################################

def natural_numbers():
    """returns 1, 2, 3, ..."""
    n = 1
    while True:
        yield n
        n += 1

x = natural_numbers()

for i in x:
    print(i)
    if (i > 7):
        break

print()

# ###########################################################
# You can iterate over a generator function ONLY ONCE
#
# You need to call the generator function AGAIN to repeat
# ###########################################################

print("Continue...")
for i in x:
    print(i)
    if (i > 9):
        break

print()

# ######################################################
# Another way to create generators
# ######################################################

# A second way to create generators is by using 
# "for comprehensions" wrapped in parentheses:

lazy_evens_below_20 = (i for i in lazy_range(20) if i % 2 == 0)

for i in lazy_evens_below_20:
    print(i)

print()

# ##################################################################
# Python dictionaries can produce a generator
#
#    dict.items() returns LIST of the (key,value) pairs
#    dict.iteritems()  returns a GENERATOR for the (key,val) pairs
#
# NOTE: in Python 3, items() works like iteritems()
# ##################################################################

d = { "a":3, "b":7, "d": 4 }

print( d )
print( d.items )

for x, y in d.items():		# Iterate over a GENERATOR in Python 3
    print(x, y)


# ################################################################
# enumerate( )
#
#   returns an iterator for a list with indexes prepfixed
# ################################################################

print("\nEnumerate:")

names = ["Alice", "Bob", "Charlie", "Debbie"]
x = enumerate(names)		# Returns a generator: (0 "Alice") (1 "Bob")...
print(x)

for i, j in x:
    print(i, j)



