在Haskell中定义函数的正确方法

在Haskell中定义函数的正确方法,haskell,Haskell,我是Haskell的新手,我正在尝试一些教程。 我写了这个剧本: lucky::(Integral a)=> a-> String lucky 7 = "LUCKY NUMBER 7" lucky x = "Bad luck" 我将此保存为lucky.hs,并在解释器中运行,效果良好 但我不确定函数定义。从我所读到的一点来看,我似乎同样可以将函数lucky定义为如下(函数名为lucky2): 两者似乎都同样有效。显然,函数lucky更清晰易读,但lucky2是编写函数的正确方法吗?

我是Haskell的新手,我正在尝试一些教程。 我写了这个剧本:

lucky::(Integral a)=> a-> String
lucky 7 = "LUCKY NUMBER 7"
lucky x = "Bad luck"
我将此保存为lucky.hs,并在解释器中运行,效果良好

但我不确定函数定义。从我所读到的一点来看,我似乎同样可以将函数lucky定义为如下(函数名为lucky2):


两者似乎都同样有效。显然,函数lucky更清晰易读,但lucky2是编写函数的正确方法吗?

它们都是正确的。可以说,第一个是更加惯用的Haskell,因为它使用了名为模式匹配的非常重要的特性。在这种形式下,通常写为:

lucky::(Integral a)=> a-> String
lucky 7 = "LUCKY NUMBER 7"
lucky _ = "Bad luck"
下划线表示忽略参数的确切形式(值)。您只关心它不同于
7
,这是您以前的声明捕获的模式


模式匹配的重要性最好通过对更复杂的数据(如列表)进行操作的函数来说明。例如,如果要编写一个计算列表长度的函数,您可能首先为空列表提供一个变量:

len [] = 0
[]
子句是一种模式,设置为匹配空列表。空列表的长度显然为0,因此这就是我们的函数返回值

len
的另一部分是:

len (x:xs) = 1 + len xs
这里,您正在匹配模式
(x:xs)
。冒号
是所谓的cons运算符:它在列表中添加一个值。因此,表达式
x:xs
是一种模式,它与附加到某个列表(
xs
)中的某个元素(
x
)相匹配。总体而言,它与至少有一个元素的列表相匹配,因为
xs
也可以是空列表(
[]

len
的第二个定义也非常简单。您可以计算剩余列表的长度(
lenxs
)及其1处的长度,该长度对应于第一个元素(
x

(编写上述定义的通常方式是:

len (_:xs) = 1 + len xs

这再次表明您不关心第一个元素是什么,只关心它的存在。

第三种编写方法是使用保护:

lucky n
    | n == 7    = "lucky"
    | otherwise = "unlucky"
没有理由对此感到困惑。做这件事总有不止一种方法。请注意,即使没有模式匹配或保护,并且您必须使用
if
,这也是正确的

到目前为止,我们讨论的所有形式都使用Haskell提供的所谓语法糖。模式保护被转换为普通大小写表达式,以及多个函数子句和if表达式。因此,写这篇文章的最低级、最不讲究的方式可能是:

lucky n = case n of
     7 -> "lucky"
     _ -> "unlucky"

虽然你检查一下惯用的方法是好的,但我还是向初学者推荐,他使用最适合自己的方法,用他最了解的方法。例如,如果一个人(尚未)理解无点样式,则没有理由强制它。就我所知,它们在意义上是相等的,但第一个看起来更好,是哈斯克尔的首选方法。我不会将此作为答案发布,因为我不是Haskell高手,可能是错的。顺便说一下,您不需要括号:
Lucky2x=如果x==7,那么“幸运数字7”或者“坏运气”
您也可以将类型写为
LUCKY::Integral->String
=>
左侧的命名内容只是为了防止您需要两次相同的类型。您到底在问什么?如果它有效,那么iit显然是“正确”的语法。你是在问“如果”的说法是否是惯用的吗?托马斯:这不是真的。Integral是一个类型类,而不是类型。不幸的是,您演示了一种计算列表长度的糟糕方法。Ingo,wut?甚至标准库也是这样做的:。当然,您可以对
Int
s和一些其他类型进行优化(请参见
strictGenericLength
),但它甚至不会更改渐近时间要求。
lucky n = case n of
     7 -> "lucky"
     _ -> "unlucky"