【问题标题】:What fun can be had with lambda-definitions?使用 lambda 定义可以带来什么乐趣?
【发布时间】:2011-01-18 13:19:35
【问题描述】:

没有让他们这么多使用它们,我不太确定所有的方式 可以使用 lambda 定义(除了 map/collect/do/lightweight 本地函数语法)。对于有兴趣发布一些示例的任何人:

  • 提供解释以帮助读者了解如何使用 lambda 定义;

  • 示例的首选语言:Python、Smalltalk、Haskell。

【问题讨论】:

    标签: function lambda language-agnostic closures


    【解决方案1】:

    您可以使用 lambda 构建函数式数据结构。这是一个简单的 - 一个函数列表(Python),支持addcontains 方法:

    empty = lambda x : None
    
    def add(lst, item) :
        return lambda x : x == item or lst(x)
    
    def contains(lst, item) :
        return lst(item) or False
    

    我只是为了好玩而快速编写代码 - 请注意,您不能按原样添加任何虚假值。它也不是尾递归的,因为它应该是一个好的功能结构。供读者练习!

    【讨论】:

    • 会改变它工作吗? def contains(lst, item) : 返回 lst(item) 或无
    【解决方案2】:

    您可以将它们用于控制流。例如,在 Smalltalk 中,“ifTrue:ifFalse:”方法是布尔对象的一种方法,在 True 和 False 类中的每一个都有不同的实现。表达式

    someBoolean ifTrue: [self doSomething] ifFalse: [self doSomethingElse]
    

    使用两个闭包——块,在 Smalltalk 语法中的 [方括号] 中——一个用于真分支,一个用于假分支。对于 True 类的实例,“ifTrue:ifFalse:”的实现是

    ifTrue: block1 ifFalse: block2
        ^ block1 value
    

    对于 False 类:

    ifTrue: block1 ifFalse: block2
        ^ block2 value
    

    在这里,闭包用于延迟评估,以便可以做出关于控制流的决定,根本不需要任何专门的语法(除了块的语法)。

    Haskell 有点不同,它的惰性求值模型在许多情况下有效地自动产生闭包的效果,但在 Scheme 中,您最终会大量使用 lambdas 进行控制流。例如,这是一个从关联列表中检索值的实用程序,在该值不存在的情况下提供可选计算的默认值:

    (define (assq/default key lst default-thunk)
      (cond
        ((null? lst) (default-thunk)) ;; actually invoke the default-value-producer
        ((eq? (caar lst) key) (car lst))
        (else (assq/default key (cdr lst) default-thunk))))
    

    它会被这样调用:

    (assq/default 'mykey my-alist (lambda () (+ 3 4 5)))
    

    这里的关键是使用 lambda 来延迟默认值的计算,直到实际知道需要它为止。

    另请参见 continuation-passing-style,它把这种情况发挥到了极致。例如,Javascript 依赖于持续传递样式和闭包来执行其所有阻塞操作(如休眠、I/O 等)。

    ETA:我在上面所说的闭包是指词法作用域闭包。通常,词法作用域很关键。

    【讨论】:

    • 你能添加一个词法作用域的描述吗?
    • @Roman: 词法作用域意味着如果你有像f := a -> (x -> (a++)*x) 这样的函数,调用f(2) 将返回函数x -> (a++)*xa 绑定到具有值 2。因为这是从词汇意义上定义函数的范围内的 a。然而,这个a 完全独立于f(3) 或另一个f(2) 调用返回的函数中的a,因为每个调用都会创建一个新的闭包。
    【解决方案3】:

    您可以使用 lambda 来创建 Y Combinator,这是一个接受另一个函数并返回它的递归形式的函数。这是一个例子:

    def Y(le):
        def _anon(cc):
            return le(lambda x: cc(cc)(x))
        return _anon(_anon)
    

    这是一个值得更多解释的思想大棒,但不要在这里反刍,请查看this blog entry(上面的示例也来自那里)。

    【讨论】:

      【解决方案4】:

      它是 C#,但我个人每次阅读这篇文章时都会受到启发:

      Building Data out of Thin Air - 在 C# 中实现 Lisp 的 cons、car 和 cdr 函数。它展示了如何完全使用 lambda 函数构建简单的堆栈数据结构。

      【讨论】:

        【解决方案5】:

        它与 haskell 等中的概念并不真正完全相同,但在 C# 中,lambda 构造具有(可选)编译为表示代码的 objcet 模型的能力(表达式树)而不是代码本身(这本身就是 LINQ 的基石之一)。

        这反过来又可以带来一些非常有表现力的元编程机会,例如(这里的 lambda 表示“给定一个服务,你想用它做什么?”):

        var client = new Client<ISomeService>();
        string captured = "to show a closure";
        var result = client.Invoke(
            svc => svc.SomeMethodDefinedOnTheService(123, captured)
        );
        

        (假设有合适的Invoke 签名)

        这种类型的东西有很多用途,但我用它来构建不需要任何运行时代码生成的 RPC 堆栈 - 它只是解析表达式树,找出调用者的意图,将其转换为 RPC、调用它、收集响应等(更多讨论 here)。

        【讨论】:

          【解决方案6】:

          Haskell 中使用数值近似计算单个变量函数导数的示例:

          deriv f = \x -> (f (x + d) - f x) / d
            where
              d = 0.00001
          
          f x = x ^ 2
          f' = deriv f -- roughly equal to f' x = 2 * x
          

          【讨论】:

            猜你喜欢
            • 2012-06-29
            • 2018-01-13
            • 1970-01-01
            • 2012-06-30
            • 2011-02-09
            • 1970-01-01
            • 2017-08-10
            • 1970-01-01
            • 2017-02-08
            相关资源
            最近更新 更多