解码Cryptic Python语句

时间:2014-01-15 07:47:26

标签: python

我在招聘广告上看到了这一点(在SO上):

lambda f: (lambda a: a(a))(lambda b: f(lambda *args: b(b)(*args)))

所以我理解的是它是一个匿名(未命名)函数,它由两个进一步嵌套的匿名函数组成。最里面的函数采用一个变量的争论列表(* args)。

我无法弄清楚它应该做什么。如果没有看到实际的args列表,这实际上是否有效或无法分辨?

4 个答案:

答案 0 :(得分:7)

我已经取消了lambdas只是为了让它更容易阅读。这是使用嵌套函数的代码:

def f1(f):
    def f2(a):
        return a(a)

    def f3(b):
        def f4(*args):
            return b(b)(*args)

        return f(f4)

    return f2(f3)

这基本上等同于:

f1 = lambda f: (lambda a: a(a))(lambda b: f(lambda *args: b(b)(*args)))

现在让我们按照函数调用。首先,你要用一些参数调用f1。然后将发生以下情况:

  1. 使用f3
  2. 调用f2
  3. f2将自身作为参数调用的f3返回
  4. 现在我们在f3里面,b为f3
  5. f3以f4作为参数
  6. 返回f(您调用f1的参数)
  7. f是一个使用函数作为唯一参数调用的回调
  8. 如果f调用此函数,则其调用将应用于使用b调用的b的结果。 b是f3,所以f基本上会调用f3(f3)的结果,即f将返回的结果
  9. 因此f1可以简化为:

    def f1(f):
        def f3():
            def f4(*args):
                return f3()(*args)
            return f(f4)
    
        return f3()
    

    现在我想出了一种方法来调用不以无限递归结束的f1:

    called = False
    
    def g1(func):
        def g2(*args):
            print args
            return None
    
        global called
    
        if not called:
            called = True
            func(5)
        else:
            return g2
    
    f1(g1) # prints "(5,)"
    

    如您所见,它使用全局来停止递归。

    这是另一个运行泊松分布试验的例子,其中lambda(lambda是Poisson分布的参数,而不是lambda算子)为10:

    import random
    
    def g3(func):
        def g4(a):
            def g5(b):
                print a
                return a+b
            return g5
    
        if random.random() < 0.1:
            return g4(1)
        else:
            return g4(func(1))
    
    f1(g3)
    

    最后确定性的东西,不依赖于全球,实际上有点有趣:

    def g6(func):
        def g7(n):
            if n > 0:
                return n*func(n-1)
            else:
                return 1
    
        return g7
    
    print f1(g6)(5) # 120
    print f1(g6)(6) # 720
    

    我确信每个人都可以猜出这个函数是什么,但非常有趣的是你可以让这个奇怪的lambda表达式做一些有用的事情。

答案 1 :(得分:6)

代码正在创建Y-combinator in Python。这只是一个练习,而不是现实世界的代码;不要试图解密它。

要了解Y-combinator本身的作用,您可以参考这个SO问题:What is a y-combinator?及其维基百科页面:Fixed-point combinator

也许这个广告正在寻找那些了解函数式编程和/或高级计算机科学主题的人,例如Lambda calculusCombinatory logic,它们是函数式编程背后的主要理论基础。

或许他们的公司是Y Combinator winner startup,他们只是在寻找具有CS背景的Python程序员。

答案 2 :(得分:3)

不仅仅是* args,如果不知道abf *args是什么,很难说出它是什么

当b作为参数传递给函数b时,兔子洞特别深,并且该结果是传递args的函数。

答案 3 :(得分:2)

这是完全相同的,没有太多的嵌套:

def f1(f):
    def f2(a): return a(a)
    def f3(b):
        def f4(*args): return b(b)(*args)
        return f(f4)
    return f2(f3)

f2可以替换,所以我们得到

def f1(f):
    def f3(b):
        def f4(*args): return b(b)(*args)
        return f(f4)
    return f3(f3) # was f2(f3)
始终会自行调用

f3,因此我们将b替换为f3

def f1(f):
    def f3():
        def f4(*args): return f3()(*args)
        return f(f4)
    return f3()

那它做了什么?

我们看到f3()返回f返回的内容,并且应该使用任意数量的参数调用它。

f应该使用*args获取另一个函数的函数并返回这样的函数。

调用f1(f)会返回f(f4)返回的内容。

让我们测试一下:

  • 使用f=lambda ff: lambda *args: ff(*args)f1(f)()为我们提供了无限递归。
  • f=lambda ff: lambda *args: (ff, args)没有调用给定的函数,所以我们可以安全地使用它:
    • f1(f)(4)为我们提供了一个元组(f4, (4,))
    • 如果我们使用该元组的第0个元素:f1(f)(4)[0](1),我们会得到(f4, (1,))