Haskell趣学指南学习日记(4)-强大的函数

函数基础


Haskell是一门函数式语言,他的核心用法也是围绕着函数所展开的,所以它本身也自带了一套十分强大的函数系统。

模式匹配进阶

前面我们谈到了Haskell的模式匹配,其实就是将函数的参数显示定义,然后再定义函数内容。当然,这么说很不严谨,其实可以说是错误的。因为Haskell是采用先定义函数内容的方式再来匹配参数的形式。例如:

addVectors :: (Double,Double)->(Double,Double)->(Double,Double)
addVectors a b = (fst a + fst b,snd a + snd b)

不修改匹配模式,只修改定义:

addVectors :: (Double,Double)->(Double,Double)->(Double,Double)
addVectors (x1,y1) (x2,y2) = (x1+x2,y1+y2)

他们都可以被匹配到。

元组和列表的匹配

下面通过几个例子来展现Haskell的一些施加在元组和列表上的奇淫巧技。

first :: (a,b,c)->a
first (x,_,_) = x

second :: (a,b,c)->b
second (_,y,_) = y

third :: (a,b,c)->c
third (_,_,z) = z
--对于我们不在意的参数可以直接使用`_`关键字代替
head' :: [a]->a
head' [] = error "Cant call head with empty list"
head' (x:_) = x
--用单引号可以表示意思相近的一个函数,(x:_)(x:xs)都可以用来表示列表
firstletter :: String->String
firstletter "" = "Empty string whoops!"
firstletter all@(x:xs) = "The first letter of " ++ all ++" is "++ [x]
--As模式:@符号前面表示后面括号中的总和

哨位

前面我们写了大量重复的函数名称,他们的匹配模式是相同的但参数有所不同,Haskell看到这种情况之后,又给我们发糖了(●’◡’●)

下面我们来写一个根据体重身高判断bmi值的程序,并给出反馈:

bmiTell :: Double -> String
bmiTell bmi
    | bmi<=18.5 = "你太轻了,气球!"
    | bmi<=25.0 = "你的体重很正常,不过要记住,人丑就要多读书!"
    | bmi<=30.0 = "你太重了,肥猪"
    | otherwise = "多吃点吧,马克思在等着你呢~"

where和let

这两个的用法没有多大区别,不过let是一次性的,where是有作用域的,不过作用域也不算大。

bmiTell' :: Double->Double -> String
bmiTell' weight height
    | bmi<=18.5 = "你太轻了,气球!"
    | bmi<=25.0 = "你的体重很正常,不过要记住,人丑就要多读书!"
    | bmi<=30.0 = "你太重了,肥猪"
    | otherwise = "多吃点吧,马克思在等着你呢~"
    where bmi = weight/height ^2

在这里,where的作用域是整个函数。如果定义多个临时变量,可以直接加在bmi定义的后面。另外一说,where和let都可以直接定义函数。

cylinder :: Double -> Double -> Double
cylinder r h =
        let pi = 3.14
        in
            let
                sideArea = 2*pi*r*h
                topArea = pi*r^2
            in sideArea + 2*topArea

这段代码看起来比较复杂,其实也没什么难度。let...in...的语法是一个表达式,它返回in后面的结果,而let也只是来定义一些临时变量让in后面的语句变得更加好看而已。

当然,有很多情况下,let后面也不必加上in:

calcBmis :: [(Double,Double)]->[Double]
calcBmis xs = [bmi | (w,h) <- xs,let bmi = w / h ^2, bmi > 25.0]

这个let语句没有in,但是依旧是定义了前面的bmi。

除此之外,我们也可以再ghci中直接用let定义临时变量:

函数进阶


柯里函数

可能大家会很奇怪函数匹配为什么那么蛋疼,比如参数列表为什么不能像Lambda表达式那样简介明了,比如这样的函数匹配:

max :: a->b->Bool

为什么不能改成:

max :: (a,b)->Bool

嘛,其实这样是有原因的。你可以实验一下只在max函数后面添加一个参数:


具体原因如上所示,当你没有把参数调用完全时,它会返回一个带有后面的参数的函数。从本质上来说, Haskell的函数都只有一个参数。它的调用过程则是循序渐进的。

截断

利用柯里函数的特性,让函数仅返回一个函数,然后再调用:

传入函数

在一般的静态语言中,都允许传入函数对象作为参数的,Haskell同样支持这个特性:

zipWith' :: (a->b->c)->[a]->[b]->[c]
zipWith' _ [] _ = []
zipWith' _ _ [] = []
zipWith' f (x:xs) (y:ys) = f x y :zipWith' f xs ys

这个函数接受一个函数作为参数,如果要在模式匹配中匹配函数,只需要将函数参数返回值用括号捆绑在一起即可。

lambda

lambda,既匿名函数,他的声明语法如下:
(\xs->length xs > 15)
这句话返回一个接受一个字符串,如果这个字符串的长度大于15就返回True的函数。

$运算符

这个运算符用于优先级的计算。在Haskell中,函数是左结合的,也就是说,你调用f a b c的顺序是(((f a) b) c),但是我们当我们加上dollar符时,就可以把它变成右结合了。

看一个例子:


这括号括得的确影响美观,所以我们给它加上dollar符号(好像也不咋地。。。):

函数组合

Haskell中的函数可以组合到一起调用,其实也只是右结合率的一个简写。它的运算符是.

下面这段代码:

sum (replicate 5 (max 6 7))

等价于

(sum . replicate 5 ) max 6 7

再使用dollar符简化:

sum . replicate 5 $ max 6 7

我们把使用很多点调用函数的方式称为point free风格。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值