0

Background: In Python, I'm trying writing a generator function that yields functions that are repeated applications of a one-argument function f. The first function yielded should apply f 0 times (the identity function), the second function yielded should apply f once, etc. The function's doctest string is in the following.

def repeated(f):
    """
    >>> double = lambda x: 2 * x
    >>> funcs = repeated(double)
    >>> identity = next(funcs)
    >>> double = next(funcs)
    >>> quad = next(funcs)
    >>> oct = next(funcs)
    >>> quad(1)
    4
    >>> oct(1)
    8
    >>> [g(1) for _, g in
    ...  zip(range(5), repeated(lambda x: 2 * x))]
    [1, 2, 4, 8, 16]
    """
    
def repeated(f):
    g = lambda x: x
    while 1:
        yield g
        g = lambda x: f(g(x))

Problem: In the code above, max recursion depth error on line "g = lambda x: f(g(x))". But when I change that line to "g = (lambda h: lambda x: f(h(x)))(g)", it works. Why does this happen? What's the difference between these two statements?

def repeated(f):
    g = lambda x: x
    while 1:
        yield lambda x: g(x)
        g = (lambda h: lambda x: f(h(x)))(g)

Extra Problem:The code block above almost passes the doctest, but still fails. Failed example: quad(1) Expected: 4 Got: 8

However, when I replace the "yield lambda x: g(x)" to "yield g", it passes the doctest. What's the difference between "lambda x: g(x)" and "g"?

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Lurx
  • 1
  • This issue seems like it is caused by late binding. Can you try `lambda x, f=f, g=g: f(g(x))`, and see if that solves your problem? See also: https://stackoverflow.com/questions/3431676/creating-functions-or-lambdas-in-a-loop-or-comprehension – Nick ODell Feb 12 '23 at 03:53
  • awesome! I explored the link, you are totally right, the suggestion works! – Lurx Feb 12 '23 at 04:23
  • Long story short: almost every other language uses scoped names, but python uses dynamic names instead. Confusion ensues. – Stef Feb 12 '23 at 08:08

0 Answers0