繁体   English   中英

普通功能和 lambda Haskell 功能有什么区别?

[英]What is the difference between normal and lambda Haskell functions?

我是 Haskell 的初学者,我一直在关注电子书Get Programming with Haskell

我正在学习使用 Lambda 函数的闭包,但我看不到以下代码的区别:

genIfEven :: Integral p => p -> (p -> p) -> p
genIfEven x = (\f -> isEven f x)

genIfEven2 :: Integral p => (p -> p) -> p -> p
genIfEven2 f x = isEven f x

如果有人能解释这里的确切区别是什么,那就太好了

在基本级别1中,“普通”函数与使用 lambda 语法创建的函数之间没有真正的区别。 是什么让你觉得问它是什么有区别? (在您展示的特定示例中,函数以不同的顺序获取参数,但其他方面相同;它们中的任何一个都可以使用 lambda 语法或“普通”语法定义)

函数是 Haskell 中的第一个 class 值。 这意味着您可以将它们传递给其他函数,将它们作为结果返回,在数据结构中存储和检索它们等等。就像您可以使用数字、字符串或任何其他值一样。

就像数字、字符串等一样,使用语法来表示 function 值很有帮助,因为您可能想在其他代码中间创建一个简单的值。 如果您需要将x + 1传递给某个 function 并且您不能只将文字1写为第一个,那将是非常可怕的,您必须改为 go 在文件的其他位置并添加one = 1绑定这样你就可以回来写x + one 以完全相同的方式,您可能需要将 function 传递给其他一些 function 以添加 1; go 在文件的其他地方添加一个单独的定义plusOne x = x + 1会很烦人,所以 lambda 语法为我们提供了一种编写“函数文字”的方法: \x -> x + 1 2

考虑“正常” function 定义语法,如下所示:

incrementAllBy _ [] = []
incrementAllBy n (x:xs) = (x + n) : xs 

在这里,我们没有任何源代码只代表incrementAllBy是一个名称的 function 值。 function隐含在此语法中,分布在(可能)多个“规则”上,说明我们的 function返回什么值,因为它应用于 arguments 的某种形式。 这种语法也从根本上迫使我们将 function 绑定到一个名称。 所有这些都与 lambda 语法形成对比,后者仅直接表示 function 本身,没有捆绑案例分析或名称。

然而,它们只是写下函数的不同方式。 一旦定义,函数之间就没有区别,无论您使用哪种语法来表达它们。


你提到你正在学习闭包。 目前还不清楚这与问题有何关系,但我猜这有点令人困惑。

我要在这里说一些有点争议的事情:你不需要了解闭包。 3

闭包是使诸如incrementAllBy n xs = map (\x -> x + n) xs工作的东西所涉及的。 此处创建的 function \x -> x + n取决于n ,这是一个参数,因此每次调用incrementAllBy时它都可能不同,并且可以同时运行多个此类调用。 所以这个\x -> x + n function 不能像顶级函数那样最终成为程序二进制文件中特定地址处的一段编译代码。 memory 中传递给map的结构必须存储n的副本或存储对它的引用。 这样的结构称为“闭包”,据说已经“关闭”了n或“捕获”了它。

在 Haskell 中,您无需了解这些。 我将表达式\n -> x + n视为简单地创建一个新的 function 值,具体取决于恰好在范围内的值n (以及值+ ,这也是一等值。)。 我认为您无需考虑这一点,而无需考虑表达式x + n根据本地n创建新数值。 Haskell 中的闭包仅在您尝试了解该语言的实现方式时才重要,而不是在您在 Haskell 中编程时。

闭包在命令式语言中确实很重要。 是否(相当于) \x -> x + n存储对n的引用或n的副本(以及何时获取副本,以及多深)对于理解使用此 function 的代码如何工作至关重要,因为n不仅仅是一种引用值的方式,它是一个随着时间的推移(可能)具有不同值的变量。

但是在 Haskell 中,我真的不认为我们应该教初学者关于闭包的术语或概念。 它过于复杂了“您可以从现有值中创建新功能,即使是仅在本地范围内的功能”。

因此,如果您已将这两个函数作为示例来尝试说明闭包的概念,并且对您而言,这种“闭包”有什么不同是没有意义的,您可能会忽略整个问题并继续讨论更多问题重要的。


1有时,您用于编写代码的“等效”语法选择确实会影响操作行为,例如性能。 通常(但并非总是)影响可以忽略不计。 作为初学者,我强烈建议暂时忽略这些问题,所以我没有提到它们。 一旦你对所有语言元素的含义有了透彻的理解,学习推理代码如何执行所涉及的原则就会容易得多。

它有时也会影响 GHC 推断类型的方式(实际上它们实际上不是 lambda,但如果您在没有语法参数的情况下绑定 function 名称,如plusOne = \x -> x + 1 ,则可以解除单态限制,但这是许多 Stack Overflow 问题中涉及的另一个主题,所以我不会在这里讨论)。

2在这种情况下,您还可以使用运算符部分将更简单的 function 文字编写为(+1)

3现在我将教你关于闭包的知识,这样我就可以解释为什么你不需要了解闭包。 :P

没有任何区别,除了一个: lambda 表达式不需要名称。 因此,它们有时在其他语言中被称为“匿名函数”。

如果您打算经常使用 function,您需要给它一个名称,如果您只需要它一次,lambda 通常会这样做,因为您可以在使用它的站点上定义它。

当然,您可以在它出生命名一个匿名 function:

genIfEven2 = \f x -> isEven f x

那将完全等同于您的定义。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM