zyguan
6/20/2016 - 2:13 PM

rungen.py

from inspect import isgenerator

def fact(n):
    if n <= 1:
        return n
    else:
        f = yield fact(n-1)
        return f * n

def fib(n):
    if n <= 1:
        return n
    else:
        f_1 = yield fib(n-1)
        f_2 = yield fib(n-2)
        return f_1 + f_2

def xfib(n):
    if n <= 1:
        return n
    else:
        f_1 = yield from xfib(n-1)
        f_2 = yield from xfib(n-2)
        return f_1 + f_2

def fn(gen):
    return cons(None, cons(gen, none()))

def none():
    return ()

def cons(x, xs):
    return (x, xs)

def head(xs):
    return xs[0]

def rest(xs):
    return xs[1]

def empty(xs):
    return xs == none()

def run(xs):
    if empty(xs): return xs
    val, gens = xs

    if empty(gens): return xs
    ret = go(head(gens), val)

    if isgenerator(ret):
        return cons(None, cons(ret, gens))
    else:
        return cons(ret, rest(gens))

def go(gen, val):
    try:
        return gen.send(val)
    except StopIteration as e:
        return e.value

def inspect_run(gen):
    f = fn(gen)
    while not empty(rest(f)):
        print("run {} <- {}".format(head(rest(f)), head(f)))
        f = run(f)
    print("return {} by {}".format(head(f), gen))

if __name__ == "__main__":
    inspect_run(fact(4))
    inspect_run(fib(4))
    inspect_run(xfib(4))