我最近开始在讲座中学习Haskell,但是当我试图找到进一步解释以了解它如何在线工作时,我发现代码看起来完全不同。注意:我之前只学过C,并习惯了大多数类似格式的代码,看起来类似。
例如,我讲座中数字阶乘的函数如下所示:
{-**********-}
fac :: Int -> Int
fac n
| n==0 = 1
| n>0 = n * fac (n-1)
{-**********-}
但是当我在网上看起来它看起来完全不同,而且更加简单。例如:
factorial 1 = 1
factorial k = k * factorial (k-1)
任何人都可以解释为什么我所教的代码更有效,特别是在它所说的
fac:: Int->Int
为什么这很重要?
答案 0 :(得分:3)
你要问的是函数类型签名,就像C中的函数原型声明一样。具体的行说明函数fac
正在取Int
参数并返回{{1}结果。这在Haskell中并不总是必要的,因为它可以推断出使用函数的上下文类型。其他差异只是处理案例的不同方法。第一个叫做 guards ,第二个是模式匹配。
一般来说,我强烈推荐这个资源,以便轻松有趣的Haskell学习:Learn You a Haskell for Great Good!
更新:
只是简单解释为什么在这种特定情况下类型签名是必不可少的。假设你有两个不同的功能,一个带签名,另一个没有:
Int
然后,如果我们尝试执行类似factorial1 :: Int -> Int
factorial1 n
| n==0 = 1
| n>0 = n * factorial1 (n-1)
factorial2 1 = 1
factorial2 k = k * factorial2 (k-1)
的操作,haskell将抛出错误,因为它知道factorial1 3.5
不是Int。但是对于3.5
haskell只知道我们正在使用数字,factorial2
就好了。但是对于输入3.5
,它将以无限循环结束,因为它永远不会达到边界条件(k = 1)。这就是有时候签名必不可少的原因。有时相反,你希望你的函数对于不同的类型是通用的,但在这种情况下你仍然应该有一些签名定义一些更通用的类型约束(haskell有这样的工具)。
答案 1 :(得分:0)
fac:: Int->Int
是函数的类型签名。它接受Int类型的参数并返回Int。
这些对文档很有用。
Haskell能够自行推断类型,请参阅以下链接:https://www.haskell.org/haskellwiki/Type_inference
"类型推断是类型系统的一个特征,这意味着具体类型是由类型系统推导出来的,而且很明显"
这意味着我们不必提供类型签名。
第一个例子使用一种叫做守卫的东西。有关警卫的更多信息,请参阅此链接: http://learnyouahaskell.com/syntax-in-functions#guards-guards
第二个使用模式匹配。 (参见上面的链接,向上滚动到模式匹配)
在这种情况下,这只是解决同一问题的两种不同方式。
答案 2 :(得分:0)
这两种方法都没有任何好处或坏处。他们只是一个偏好的问题。话虽这么说,两个函数都应该有类型签名Int -> Int
(即使它们在两种情况下都是可选的),因为它提高了可读性,如果你做错了,类型检查器会抱怨。因此,这两个代码实际上是相同的:您在第一个版本中使用了防护,在第二个版本中使用了模式匹配。
此外,this question为类型签名对两个函数都有好处提供了一些好处。
答案 3 :(得分:0)
如果您想了解有关这些类型签名的更多信息,在GHCI或您自己的程序中使用:t工具非常有用。如果您对为什么会遇到类型与预期类型不匹配的错误感到好奇,您可以随时查看函数的类型。所以在GHCI中输入:t take或:t repeat:t复制......你明白了。当你进入列表和元组时帮助很多......