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

9 haskell

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

lucky::(Integral a)=> a-> String
lucky 7 = "LUCKY NUMBER 7"
lucky x = "Bad luck"
Run Code Online (Sandbox Code Playgroud)

我把它保存为lucky.hs并在解释器中运行它并且工作正常.

但我不确定函数定义.从我读过的小小看来,我可以同样定义幸运函数如下(函数名称为lucky2):

lucky2::(Integral a)=> a-> String
lucky2 x=(if x== 7 then "LUCKY NUMBER 7" else "Bad luck")
Run Code Online (Sandbox Code Playgroud)

两者似乎同样有效.显然,功能幸运是清晰的阅读,但是是lucky2编写一个函数以正确的方式?

Xio*_*ion 15

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

lucky::(Integral a)=> a-> String
lucky 7 = "LUCKY NUMBER 7"
lucky _ = "Bad luck"
Run Code Online (Sandbox Code Playgroud)

下划线表示您忽略参数的确切形式(值).您只关心它与7您之前的声明所捕获的模式不同.


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

len [] = 0
Run Code Online (Sandbox Code Playgroud)

[]子句是一个模式,设置为匹配空列表.空列表显然长度为0,所以这就是我们的函数返回.

另一部分len将是以下内容:

len (x:xs) = 1 + len xs
Run Code Online (Sandbox Code Playgroud)

在这里,你匹配模式(x:xs).冒号:是所谓的cons运算符:它将值附加到列表中.x:xs因此,表达式是一个模式,它匹配一些x附加到某个list(xs)的element ().总的来说,它匹配一个至少包含一个元素的列表,因为xs它也可以是一个空列表([]).

第二个定义len也非常简单.您计算剩余list(len xs)的长度和1 的距离,它对应于第一个元素(x).

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

len (_:xs) = 1 + len xs
Run Code Online (Sandbox Code Playgroud)

这再次表明你不关心第一个元素是什么,只是它存在).

  • Ingo,哇?即使是标准.库是这样做的:[genericLength](http://www.haskell.org/ghc/docs/latest/html/libraries/base/src/Data-List.html#genericLength).您可以对`Int`s和其他一些类型进行优化,确定(参见`strictGenericLength`),但它甚至不会改变渐近时间要求. (5认同)

Ing*_*ngo 7

第三种方式是使用警卫:

lucky n
    | n == 7    = "lucky"
    | otherwise = "unlucky"
Run Code Online (Sandbox Code Playgroud)

没有理由对此感到困惑.总有一种方法可以做到这一点.请注意,即使没有模式匹配或防护,您必须使用if.

到目前为止,我们所涵盖的所有形式都使用了Haskell提供的所谓的语法糖.模式保护转换为普通的案例表达式,以及多个函数子句和if表达式.因此,写这篇文章的最低级,未经处理的方式可能是:

lucky n = case n of
     7 -> "lucky"
     _ -> "unlucky"
Run Code Online (Sandbox Code Playgroud)

虽然你检查一下惯用的方式是好的,但我建议初学者使用对他来说最好的东西,无论他最了解什么.例如,如果一个(尚未)理解点自由风格,则没有理由强制它.它迟早会来找你.