Date
Dec. 27th, 2024
 
2024年 11月 27日

Post: Haskell Primer 003: 函数定义

Haskell Primer 003: 函数定义

Published 12:04 Apr 12, 2016.

Created by @ezra. Categorized in #Programming, and tagged as #Haskell.

Source format: Markdown

Table of Content

尝试了一些函数调用之后,紧接着了解一下函数的定义,这与调用的形式类似,只是多了函数行为的定义:

函数名 参数1 参数2 参数3 参数4 ... = 函数行为

例如:

doubleIt x = x * 2
doubleThem x y = (x + y) * 2

将这两个函数定义语句保存在一个 .hs 文件中,例如 /Users/Meniny/func.hs

然后将终端工作目录切换到 (cd) 该目录,进入 GHCi 并执行 :l func 命令:

Haskell> :l func
[1 of 1] Compiling Main             ( func.hs, interpreted )
Ok, modules loaded: Main.
Haskell> doubleIt 9
18
Haskell> doubleThem 2 3
10
Haskell> 2 `doubleThem` 3
10
Haskell> doubleThem 2 3 + doubleIt 9
28
Haskell>

与主流语言 (例如 C) 不同的是,在我们的 func.hs 中,函数定义的顺序对程序并没有影响。

此外,你当然也可以在你的函数行为定义中调用其它函数,比如我们的 doubleThem 还可以这样写:

doubleThem x y = doubleIt x + doubleIt y

在实际应用中,函数行为并不会总是这么简单,下面我们尝试一些复杂的函数:

amIOldEnough x = if x >= 18
                then True
                else False

这个函数可以判断你输入的年龄是否大于或等于 18:

Haskell> amIOldEnough 18
True
Haskell> amIOldEnough 10
False
Haskell>

与多数语言不同的是,else 部分是不可省略的,因为 Haskell 程序事实上是一个函数的集合,这些函数将数据作为参数,处理转换为结果,也就是说,这里的 if 事实上是一个必然返回结果的表达式 (Expression),而不是语句 (Statement)。

当然,那些换行是不必要的。

伴随着函数定义出现的,通常还有函数类型说明,也叫声明:

函数名 :: 参数类型1 -> 参数类型2 ... -> 参数类型n -> 返回类型

例如,一个求整数平方的函数:

square :: Integer -> Integer
square n = n * n

这写代码表明,square 函数接受一个整数参数,处理后返回一个整数结果,其处理过程为将输入参数乘以自身,即平方。

类似的,也可以接受更多参数:

squaresum :: Integer -> Integer -> Integer
squaresum x y = x * x + y * y

对于函数名,在 Haskell 中使用单引号 ' 是合法的,例如:

someFunction' x = x + 1
another'function x = x + 2
give'me'a'strng = "This is Ezra."

使用起来并没有什么不同:

Haskell> someFunction' 2
3
Haskell> another'function 3
5
Haskell> give'me'a'string
"This is Ezra."
Haskell>

但是,通常 ' 符号被用来区分这是某个函数的严格求值版本 (相对于惰性求值),或稍有不同的版本。

此外,为了应对更多种情况,我们也会使用守卫 (guard) 来进行区分:

函数名 参数1, 参数2 ... 参数3
    | 守卫条件1 = 结果1
    | 守卫条件2 = 结果2
    ...
    | otherwise = 结果n

例如:

max :: Integer -> Integer -> Integer
max x y
      | x >= y   =x
      |otherwisr = y

有些情况下,在 guard 条件中,可能多次进行重复的计算:

arealevel :: Double -> Double -> String
arealevel w h
    | w * h <= 1 = "Tiny"
    | w * H <= 10 = "Small"
    | w * H <= 100 = "Normal"
    | w * h <= 1000 = "Big"
    | otherwise = "Huge"

显然,仅仅是本例中简单的 w * h 计算,多次书写也已经十分繁琐了,更不方便代码维护。这时候,就要用到 where:

arealevel :: Double -> Double -> String
arealevel w h
    | a <= 1 = "Tiny"
    | a <= 10 = "Small"
    | a <= 100 = "Normal"
    | a <= 1000 = "Big"
    | otherwise = "Huge"
    where a = w * h

最后,你也许已经注意到了,我们的函数定义都以小写字母开头,这是因为 Haskell函数名不能以大写字母开头

Pinned Message
HOTODOGO
The Founder and CEO of Infeca Technology.
Developer, Designer, Blogger.
Big fan of Apple, Love of colour.
Feel free to contact me.
反曲点科技创始人和首席执行官。
开发、设计与写作皆为所长。
热爱苹果、钟情色彩。
随时恭候 垂询