【问题标题】:Practice part of lambda abstractions练习部分 lambda 抽象
【发布时间】:2011-07-14 23:56:33
【问题描述】:

我是 Haskell 编程的新手。现在我正在学习 lambda 函数和 lambda 抽象。我想,lambda函数的实践部分是什么。例如我们有:

map(\x -> x * 2) [1,2,3,4,5]
[2,4,6,8,10]

这个练习部分是什么?我们可以创建普通函数并将其设置在 map 函数参数中。只是为了减少代码?那么为什么会出现这种抽象。代码减少不是什么大问题。为什么?

谢谢。

【问题讨论】:

  • 我相信维基百科会回答你的问题。例如,请参阅Functional programming
  • s/练习部分/实际目的/?
  • 别忘了你可以写得更简洁:map (*2) [1..5]
  • 简短的回答是“你为什么不呢?”很明显,您需要它,但它最终会让您的生活更轻松。最后,它与列表的文字语法一样出色,而不是0:1:2:[]。我在my answer to this SO question about lambdas in Ruby 中说了一些类似的话。

标签: haskell abstraction lambda


【解决方案1】:

有两种方法可以解释这个问题。首先是为什么我们可以写map (\x -> x * x)

doubleIt x = x * x
... map doubleIt ...

答案是你可能有以下情况:

foo y zs = map (\x -> x^y) zs

然后没有完全直接的转换可以让您将匿名函数完全浮动到顶层。

我们可以写

foo y zs = map (powerIt y) xs
powerIt y = ?

然后我们发现 powerIt y = \x -> x^y ! (当然,您可以将 x 浮动回外部定义,但在这种情况下,您实际上想要(尽管从语法上 Haskell 不会让您)将 (powerIt y) 写为 (\x -> powerIt y x)

当你有一流的函数时,它们需要关闭它们的环境,这意味着你需要某种方法来引入一个可以引用事物的函数声明它的词法范围。这就是 lambda 的力量!

现在,另一个问题是为什么不将匿名函数浮动到不是顶部的适当级别并因此得到,例如

foo y zs = let powerIt x = x^z
           in map powerIt zs

在这种情况下,请仔细考虑一下 let 的真正含义。实际上,我们可以通过以下方式将 let 转换为几个 lambda:foo ys zs = (\powerIt -> map powerIt zs) (\x -> x^y)

现在,在实际实现中,这些脱糖操作并非都以这种方式发生,有时编译器会出于效率方面的原因对不同的子句进行不同的处理。但是核心仍然存在——lambdas 非常强大和昂贵,并且可以为我们提供一种简单且易于推理的核心语言,尽管我们在它们之上叠加了各种绑定形式。

如果没有 lambda,我们就有了一种可以定义变量和函数的语言,然后我们有时可以用函数做某些事情。有了 lambda,我们就有了一种语言,其中函数就像任何其他值一样是值,并且我们有一套统一的方法来为任何东西分配名称。

视角的反转是不将 lambdas 视为一种特殊情况,而是将其视为一般情况,并将我们所有其他各种名称绑定机制视为它们的糖。

【讨论】:

  • 我应该注意到,从历史上看,为什么 let 不转换为 lambdas 的原因是 let 绑定是多态的,而 lambda 函数的参数默认情况下是单态的。但是现代 ghc 正在消除 let 绑定的默认泛化,这使得转换更加直接。
【解决方案2】:

并非所有功能都被广泛使用以“应得”一个名称。例如,如果您只使用此功能一次,对于此特定地图,您可能不想为其指定名称,因为您将永远不会再次使用它。

【讨论】:

    【解决方案3】:

    您经常遇到这样的情况,即您需要一个非常专业的功能。它在您软件的这一部分中使用,并且仅在此处使用。因此,给它一个具体的名称是没有任何意义的。

    【讨论】:

      【解决方案4】:

      可读性;你可以有(牵强,但为了说明这一点)

      map (\x -> x * 2) . anotherfunction . anotheragain . itgoesonandon $
      [ a lont list ]
      

      map f . anotherfunction . anotheragain . itgoesonandon $
      [ a lont list ]
        where f x = x * 2
      

      这是个人喜好问题,但在第一种情况下,您马上就知道要映射什么函数,在第二种情况下,您必须在源代码中找到 f,而且它可能在几行之外。

      顺便说一句,我会写map (*2) ....反正

      另一个例子是脱糖的一元符号。

      例如,

      do a <- action1
         b <- action2
         action3
         c <- action4 a b
         action5 c
      

      变成:

      action1 >>= \a -> action2 >>= \b -> action3 >> action4 a b >>= \c -> action5 c
      

      对比

      action1 >>= a2
        where a2 a = action2 >>= a3
                 where a3 b = action3 >> action4 a b >>= a4
                         where a4 c = action5 c
      

      (该代码可能有错误,但无论如何我都不会写)

      【讨论】:

        【解决方案5】:

        如果我正确理解您的问题,您是在问为什么有人会提出超出特定编程语言范围的 Lambda 函数抽象,对吗?

        嗯,编程语言中的 Lambda 函数派生自 Lambda calculus,这是一个函数定义的正式系统。正如维基百科所说,Lambda 演算是由 Alonzo Church 在 1930 年代引入的,作为对数学基础的调查的一部分。它有广泛的应用,不仅仅是在计算机科学中。

        正如上面一位评论者所说,Functional programming 源于 Lambda 演算。我只能建议你阅读维基百科中的相关条目,那里有一些非常有趣的信息;-)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-08-23
          • 1970-01-01
          • 1970-01-01
          • 2010-09-19
          • 1970-01-01
          相关资源
          最近更新 更多