haskell學(xué)習(xí)

工具

haskell platform虏冻,直接百度安裝.

打開控制臺輸入ghci即進(jìn)入交互模式况鸣。

假如定義了myfunction.hs,在ghci中輸入:l myfunction.hs便會進(jìn)行加載∏┰撸‘

細(xì)節(jié)

  • 函數(shù)的優(yōu)先級比運(yùn)算高,如succ 2*3會先計算succ 2
  • 非運(yùn)算使用not
  • 真值用True绢馍,假值用False,注意開頭大寫肠套。
  • /=表示不等
  • 取余用的是mod
  • 單行注釋使用--舰涌,多行注釋使用{- -}

初學(xué)者第一個函數(shù)

doubleMe x = x * x.
創(chuàng)建test.hs,鍵入以上函數(shù)你稚,加載方式為::l test.hs瓷耙,之后就可使用。
也可以在test.hs鍵入兩行函數(shù):

doubleMe x y = x*y + x*y
doubleUs x y = doubleMe x y + doubleMe x y

之后再重新加載刁赖,兩個函數(shù)都可以使用搁痛。

在haskell中if then else是一種表達(dá)式。如

doubleSmallNumber x = if x>100 then x else x*2

可以看到宇弛,then esle是不可省略的鸡典,必須有一個確定的最終值。

首字母大寫的函數(shù)是不允許的枪芒。

類似下面的沒有參數(shù)的函數(shù)彻况,其實(shí)就是定義了一個常量字符串:

someName = "hahahhahahahahahah..."

list

在ghci下使用let定義一個常量

let a = 1
  • 字符串"aaaa"其實(shí)就是list的語法糖=> ['a','a','a','a']
  • list中的所有的元素的數(shù)據(jù)類型必須相同。
  • list中是通過++進(jìn)行合并操作舅踪。[1,2,3] ++ [4,5,6].但注意使用++進(jìn)行合并字符串的時候纽甘,其會遍歷++左邊的字符串,如果左邊字符串較長抽碌,會浪費(fèi)很長時間贷腕,這個時候,可以使用:運(yùn)算符,表示插入操作泽裳,如:1 : [2,3,4,5]
  • [1,2,3]實(shí)際是1:2:3:[]的語法糖瞒斩,[]表示空list
  • 按照索引取list中的元素,可以使用!!,如:"im ok"!!0會打印出i字符
  • list可以比較大小涮总,但是會從前往后依次比較胸囱,直到遇到不等的關(guān)系
  • head取list頭,tail取除head外的數(shù)據(jù)瀑梗,last去list的尾烹笔,init取除last外的數(shù)據(jù)
  • length返回list的長度,null檢查list是否為空抛丽,reverse將一個list反轉(zhuǎn)谤职。take返回一個list的前幾個元素,如:take 3 [1,2,3,4,5],drop與take用法大體相同,會刪除list的前幾個元素亿鲜。maximum返回list中最大的元素允蜈,minimum返回最小,sum,elem判斷l(xiāng)ist中是否有某個元素蒿柳,使用中綴形式饶套。

range

  • [1..20]表示從1到20的list
  • ['a'..'z']表示從a到z的list
  • [1,3..20]表示[1,3,5,7,9,11,13,15,17,19]
  • 不推薦range使用浮點(diǎn)數(shù)
  • take 24 ([2,4..])可以獲取24個2往后的偶數(shù),和[2,4..2*24]是一樣的垒探,但是前者好點(diǎn)
  • take 10 (cycle [1,2,3]),cycle表示某個列表的循環(huán)
  • take 10 (repeat 5)妓蛮,repeat表示某個元素的循環(huán)。另一種簡便方法是replicate 3 10 => [10,10,10]

list comprehension

定義集合的操作

  • [x*2 | x <- [1..10]]
  • 有條件的集合:[x*2 | x<-[1..10],x*2 >= 12],逗號隔開
  • 取50到100間除7余3的數(shù): [x | x<-[50..100],x`mod`7 == 3]
  • 偶數(shù)轉(zhuǎn)換為even,基數(shù)為odd:[if x `mod` 2 == 0 then "even" else "odd" | x <- [1..10]]
  • 多個限制條件:[ x | x <- [10..20], x /= 13, x /= 15, x /= 19]
  • 多個元素:[ x*y | x <- [2,5,10], y <- [8,10,11]]
  • length' xs = sum [1 | _ <- xs] 表示獲取xs列表的長度圾叼,其中_表示不關(guān)心當(dāng)前值蛤克。
  • 嵌套list:let xxs = [[1,3,5,2,3,1,2,4,5],[1,2,3,4,5,6,7,8,9],[1,2,4,2,1,6,3,1,3,2,3,6]] => [ [ x | x <- xs, even x ] | xs <- xxs]

tuple

tuple是元組。

  • list中的元素數(shù)據(jù)類型必須相同夷蚊,但是tuple中的元素數(shù)據(jù)類型不必相同
  • [(1,2),(8,11,5),(4,5)]會報錯咖耘,因?yàn)?code>(1,2)和(8,11,5)不是相同類型。而(1,2)(4,5)是相同類型
  • tuple可以用來表示某個人的一系列信息撬码,如("zhangsan","henan",19)
  • tuple中的項(xiàng)的數(shù)目是確定的,不允許追加
  • fst取tuple(二元組)的首元素版保,snd取tuple(二元組)的尾元素
  • zip函數(shù)將兩個交叉list生成tuple形式的list:zip [1,2,3,4,5] [5,5,5,5,5]=>[(1,5),(2,5),(3,5),(4,5),(5,5)]

type

  • 在ghci中使用:t來獲取任何表達(dá)式的類型.如:t 'a'輸出'a'::Char
  • 凡是明確的類型呜笑,其首字母必須為大寫的字母,所以一般對于函數(shù)來說彻犁,首字母不能大寫叫胁。
  • 函數(shù)也有類型,定義函數(shù)的時候汞幢,加上參數(shù)的類型和輸出類型是好習(xí)慣驼鹅,如:removeNonUppercase :: [Char]->[Char]表示輸入的是字符串,輸出還是字符串。其中[Char]String是等價的输钩。如果是多個參數(shù)豺型,則使用以下形式:addThree :: Int -> Int -> Int -> Int表示輸入三個整形,輸出1個整形买乃。
  • Integer也表示整數(shù)姻氨,但是是無界的,所以可以表示大數(shù)
  • 某些函數(shù)定義的時候傳入的不是參數(shù)剪验,而是a,b這種肴焊,這些表示類型參數(shù),表示可以傳入任何類型功戚。如:t head=>head::[a] -> a
  • :t (==)可以查看==的類型娶眷,輸出為(==) :: Eq a => a -> a -> Bool,=>為類型約束,表示a這種類型應(yīng)該為Eq類型啸臀,即相同類型届宠。

typeclass

(一)

  • 可以把typeclass想像為java中的interface
  • Eq表示可判斷相等性的類型,除函數(shù)以外所有類型都屬于Eq
  • Ord可包含比較大小的類型
  • Show除函數(shù)以外所有類型都是show類型壳咕,show函數(shù)可以取任意Show類型轉(zhuǎn)換為字符串席揽。
  • Readread函數(shù)讀取字符串轉(zhuǎn)換為某Read成員類型。如read "5" + 5 ,read "5"::Int這種方式可以指定轉(zhuǎn)換的類型谓厘,若不知道類型的情況下幌羞。其中::Int表示類型注釋,明確前邊的類型竟稳。
  • Enum表示可枚舉属桦,好處是可以使用succ,pred等函數(shù)來取得上一個,下一個他爸。
  • Bounded表示成員有上下界聂宾。如minBound :: Int => -2147483648
  • Num所有數(shù)字類型
  • Integral所有整數(shù)類型,如Int,Integer即為Integral類型
  • Floating 同上诊笤。FloatDouble為該類型
    (二)
  • class關(guān)鍵字可以創(chuàng)造一個typeclass

函數(shù)

succ 6 輸出7 表示某個值的后繼
min 4 5,max 4 5.

模式匹配

其實(shí)就是類似switch
如下代碼:

lucky::(Integral a)=> a -> String
lucky 7 = "its 7"
lucky x = "its not 7"

如果匹配到7系谐,則后續(xù)不執(zhí)行。如果未匹配到讨跟,則所傳參數(shù)綁定到x上纪他。但是如果是下面的代碼:

lucky::(Integral a)=> a -> String
lucky x = "its not 7"
lucky 7 = "its 7"

則報錯,因lucky 7 已經(jīng)被加載晾匠,lucky x已經(jīng)包含了lucky 7茶袒。

可以通過這種方式實(shí)現(xiàn)遞歸:

factorial :: (Integral a) => a -> a
factorial 0 = 1
factorial n = n * factorial (n - 1)

_符號表示不關(guān)心值,如:

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

可以通過:來匹配List,因?yàn)?code>[a,b,c]本來就是a:b:c:[]的語法糖
a:b會將[1,2,3]匹配成1:[2,3]凉馆,而如果匹配單元素List薪寓,可以寫為(x:[])亡资,匹配雙元素List:(x:y:[]),也可以不加括號向叉,寫為[x][x,y]锥腻,但是(x:y:_)必須加括號,注意此處加上括號并不是表示tuple.因?yàn)?[1,2,3])還是一個數(shù)組植康,單元素的tuple其實(shí)毫無意義旷太。
實(shí)現(xiàn)head:

head' :: [a] -> a
head' [] = error "Can't call head on an empty list, dummy!"
head' (x:_) = x

error是一個函數(shù),會導(dǎo)致程序崩潰销睁。

實(shí)現(xiàn)length:

length' :: (Num b) => [a] -> b
length' [] = 0
length' (_:xs) = 1 + length' xs

xs@(x:y:ys)類似這種形式的模式供璧,xs就表示整體,如:

capital :: String -> String
capital "" = "Empty string, whoops!"
capital all@(x:xs) = "The first letter of " ++ all ++ " is " ++ [x]

guard

類似if語句冻记,模式匹配是匹配值睡毒,而guard則匹配bool

bmiTell :: (RealFloat a) => a -> String
bmiTell bmi
    | bmi <= 18.5 = "You're underweight, you emo, you!"
    | bmi <= 25.0 = "You're supposedly normal. Pffft, I bet you're ugly!"
    | bmi <= 30.0 = "You're fat! Lose some weight, fatty!"
    | otherwise = "You're a whale, congratulations!"

與模式匹配不同的是,guard模式是通過判斷表達(dá)式的真假來運(yùn)作的冗栗。直到遇到一個為真的表達(dá)式演顾,并且|必須與前邊有縮進(jìn)。

在定義函數(shù)的時候如func a b也可以這么定義a `func` b
guard也可以和模式匹配進(jìn)行配合隅居,如果guard沒有匹配到結(jié)果钠至,后續(xù)沒有代碼則報錯胎源,但是如果后續(xù)還有模式匹配的代碼則繼續(xù)執(zhí)行棉钧,比如實(shí)現(xiàn)take:

myTake :: (Num b, Ord b) => [a] -> b -> [a]
myTake _ b
        | b <= 0 = []
myTake [] _ = []
myTake (x:xs) b = x : myTake xs (b-1)

guard后邊跟著模式匹配,代碼不會報錯涕蚤。

where

在guard模式中宪卿,可以通過where來引用某個復(fù)雜的變量值,這樣就不用重復(fù)出現(xiàn)某個復(fù)雜的表達(dá)式了万栅。如:

bmiTell :: (RealFloat a) => a -> a -> String
bmiTell weight height
    | bmi <= 18.5 = "You're underweight, you emo, you!"
    | bmi <= 25.0 = "You're supposedly normal. Pffft, I bet you're ugly!"
    | bmi <= 30.0 = "You're fat! Lose some weight, fatty!"
    | otherwise = "You're a whale, congratulations!"
    where bmi = weight / height ^ 2

where也支持模式匹配佑钾,如:

where bmi = weight / height ^ 2
    (skinny, normal, fat) = (18.5, 25.0, 30.0)

所以下面的代碼不難理解:

initials :: String -> String -> String
initials firstname lastname = [f] ++ ". " ++ [l] ++ "."
    where (f:_) = firstname
        (l:_) = lastname

where也可以定義函數(shù):

calcBmis :: (RealFloat a) => [(a, a)] -> [a]
calcBmis xs = [bmi w h | (w, h) <- xs]
    where bmi weight height = weight / height ^ 2

let

格式:let [bindings] in [expressions] let in是一個表達(dá)式,其值是expressions表示的值烦粒,bindings中進(jìn)行局部變量的定義休溶。與where不同的是,let in是一個表達(dá)式扰她,所以可以隨處安放兽掰,同if else then,而where是一個語法結(jié)構(gòu)义黎,一般只用在guard后綴。

maax x = let y = 1 in y

let也可以定義局部函數(shù):

[let square x = x * x in (square 5, square 3, square 2)]

定義多個名字豁跑,使用;隔開

(let a = 100; b = 200; c = 300 in a*b*c, let foo="Hey "; bar = "there!" in foo ++ bar)

使用模式匹配:

(let (a,b,c) = (1,2,3) in a+b+c) * 100

用在list中:

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

(w, h) <- xs 這里無法使用 bmi 這名字廉涕,因?yàn)樗?let 綁定的前面泻云。

case

case 是一個表達(dá)式,與switch相似:
格式為:

case expression of pattern -> result
                   pattern -> result
                   pattern -> result

如:

head xs = case xs of [] -> error "error"
                      (x:_) -> x

模式匹配本質(zhì)上就是case的語法糖。
上述代碼寫成模式匹配則為:

head [] = error "error"
head (x:_) = x

遞歸

haskell中實(shí)現(xiàn)while和for的方案就是遞歸狐蜕。

實(shí)現(xiàn)取列表中最大值:

maximum' :: (Ord a) => [a] -> a
maximum' [] = error "maximum of empty list"
maximum' [x] = x
maximum' (x:xs)
    | x > maxTail = x
    | otherwise = maxTail
    where maxTail = maximum' xs

遞歸實(shí)現(xiàn)的快排宠纯,真是特妹的優(yōu)雅!!

quicksort :: (Ord a) => [a] -> [a]
quicksort [] = []
quicksort (x:xs) =
    let smallerSorted = quicksort [a | a <- xs, a <= x]
        biggerSorted = quicksort [a | a <- xs, a > x]
    in smallerSorted ++ [x] ++ biggerSorted

如實(shí)現(xiàn)的reverse函數(shù):

myReverse :: [a] -> [a]
myReverse [] = []
myReverse (x:xs) = myReverse xs ++ [x]

一定要注意,最后一行為什么不寫成:myReverse xs : x呢层释,原因是沒有[1,2,3]:3這種寫法婆瓜,但是有[1,2,3] ++ [3]這種寫法或者1:[1,2,3]

遞歸的固定模式可以描述成這樣:先定義一個邊界條件,再定義函數(shù)贡羔,讓它從一堆元素中取一個并做點(diǎn)事情后廉白,把剩余的元素重新交給該函數(shù)。

高端函數(shù)

指可以接受函數(shù)作為參數(shù)乖寒,也可以返回函數(shù)作為結(jié)果猴蹂。

curried functions

原則上haskell的所有函數(shù)都只有一個參數(shù),定義的函數(shù)傳多個參數(shù)是怎么來的楣嘁?
所有多個參數(shù)的函數(shù)都是curried function磅轻,如func a b傳入兩個參數(shù),實(shí)際上是func a回傳了一個函數(shù)逐虚,并將b傳給該函數(shù)聋溜。
如:max::(Ord a)=> a->a->a可以看作max::(Ord a) => a -> (a -> a)
max a表示返回一個a->a類型的函數(shù)。那么max a b可以理解為 (max a) b

所以如果想要構(gòu)造一個和7比較大小的函數(shù)叭爱,直接調(diào)用max 7即可,因?yàn)閙ax 7會返回一個(a->a)的函數(shù)撮躁。 如下代碼:

max7 :: (Ord a, Num a) => a -> a
max7 = max 7

所以此時max7為(a->a)的函數(shù)。
查看以下代碼:

ghci> let multWithEighteen = multTwoWithNine 2
ghci> multWithEighteen 10
180

以上代碼可以看出涤伐,一個參數(shù)沒有傳入全的函數(shù)會返回另一個函數(shù)馒胆,等待剩余的參數(shù)傳遞完畢。

中級函數(shù)也可以返回函數(shù):

divideByTen :: (Floating a) => a -> a
divideByTen = (/10)

這個例子就可知道形如(/10) (+3) (++ "abc") (3:) (3+)都是函數(shù)凝果。
同樣的(*) (+) (++)也都是函數(shù)祝迂,不過這樣函數(shù)的參數(shù)為兩個。
(/10) 200200/10是等價的器净。而(200/) 10200/10也是等價的型雳。

以下代碼調(diào)用某個函數(shù)兩次:

applyTwice :: (a -> a) -> a -> a
applyTwice f x = f (f x)

從代碼中可以看出 第一個參數(shù)是(a->a)類型,也就是該類型的函數(shù)山害,在這括號是必須的纠俭,表示第一參數(shù)必須是一個函數(shù),第二個參數(shù)可以是任意元素浪慌,最后一個參數(shù)返回某個元素冤荆。
那么調(diào)用:applyTwice (+3) 10 其實(shí)就是((+3) ((+3) 10))

map和filter

map:

map :: (a -> b) -> [a] -> [b]
map _ [] = []
map f (x:xs) = f x : map f xs

map是結(jié)合高端函數(shù)和遞歸來實(shí)現(xiàn)的。

filter:

filter :: (a -> Bool) -> [a] -> [a]
filter _ [] = []
filter p (x:xs)
    | p x = x : filter p xs
    | otherwise = filter p xs

lambda

匿名函數(shù)权纤,樣式是\ 參數(shù) -> 函數(shù)體钓简,通常會用括號將lambda函數(shù)括起來乌妒,否則會引起歧義。
\x -> x + 3表示輸入x輸出x+3外邓。同樣lambda可以取多個參數(shù):\a b -> a + b撤蚊。再看個復(fù)雜點(diǎn)的例子:

addThree :: (Num a) => a -> a -> a -> a
addThree = \x -> \y -> \z -> x + y + z

這種樣式也是可以。

其他高級函數(shù)

foldl其實(shí)就是Java stream中的reduce
foldl是折疊损话,將一個數(shù)組從前折到后侦啸,傳入的第一個參數(shù)是函數(shù),第二個參數(shù)是初始值丧枪,第三個參數(shù)為List,
foldl 是fold left, 而foldr則是fold right,即從右邊開始折疊光涂。
foldl1foldr1則與foldlfoldr相似,不過他們的初始值為數(shù)組的第一個元素(首個或末尾)豪诲,只不過計算空List則會報錯顶捷。

foldl (\x y -> x+y) 0 [1,2,3]
foldl (+) 0 [1,2,3]
let sum = foldl (+) 0 [1,2,3] in sum [1,2,3]

因?yàn)閒old函數(shù)的特殊性,傳入List傳入Item屎篱,所以可以用來實(shí)現(xiàn)一些遍歷的庫函數(shù)服赎,如max,min等。只要滿足返回的結(jié)果不為List交播,都可以想辦法完成重虑。

scan函數(shù)與fold函數(shù)不同的是,scan會將每步的計算結(jié)果保存在List中秦士,如:

scanl (+) 0 [1,2,3,4] 輸出:[0,1,3,6]

其同樣有scanl,scanr, scanl1,scanr1等函數(shù)

$操作符

$操作符的優(yōu)先級最低缺厉,所以其可以充當(dāng)(),如sqrt 3 + 4 + 5表示根3 + 4 + 5的值隧土,如果我想取sqrt(3 + 4 +5)那么也可以寫成sqrt $ 3+4+5其首先會計算符號右邊的值提针。 同樣的,如果有一些函數(shù)的括號特別多曹傀,就可以使用符號來簡化代碼:sum (map sqrt [1..130])可以寫成sum $ map sqrt [1..130]

函數(shù)組合:

數(shù)學(xué)中的函數(shù)組合為:

image.png

在haskell中可以使用.號來表示辐脖,如:map (\x -> negate (abs x)) [5,-3,-6,7,-3,2,-19,24]表示將所有的x先獲取絕對值,然后通過negate取負(fù)皆愉,那么使用函數(shù)組合則表示為:map (negate . abs) [5,-3,-6,7,-3,2,-19,24],這樣表示的函數(shù)更為方便嗜价,但是注意函數(shù)組合的順序。
函數(shù)組合可以構(gòu)造更多的函數(shù)幕庐,形如f1 . f2 a的函數(shù)實(shí)際上和f1(f2 a)等價的久锥。

模塊

類比Java中的類,裝在模塊的方式是通過import
在某個.hs文件中使用import Data.List可以將Data.List模塊裝入异剥,這個模塊有很多操作List的方法瑟由。
如果使用ghci交互界面來裝載模塊,可以使用:m Data.List,使用:m Data.List Data.Map Data.Set裝載多個函數(shù)冤寿。其實(shí)ghci初始的時候會裝載Prelude模塊歹苦,所以你能使用到filter绿鸣,map等常用函數(shù)
import Data.List (nub,sort)只裝載nub和sort函數(shù)
import Data.List hiding (nub)除了nub函數(shù),其他都裝載
import qualified Data.Map 關(guān)鍵字qualified表示暂氯,如果調(diào)用該模塊中的某個與外部函數(shù)同名的函數(shù),就必須加上Data.Map前綴亮蛔。
import qualified Data.Map as M同名函數(shù)加M前綴即可:M.filter

Data.List

Data.List有很多方便處理List的函數(shù)痴施,如map,filter等,為了方便究流,將Data.List中的一些函數(shù)直接加入到haskell中辣吃,所以調(diào)用的時候,就不用再寫Data.List前綴芬探。
以幾個罕見函數(shù)舉例:

  • intersperse '.' "money":類似java中的join函數(shù)神得,將第一個參數(shù).加到list中每兩個元素的中間
  • intercalate " " ["I","Love","you"]:跟上邊的 函數(shù)類似,不過將第一個參數(shù)換成了list
  • transpose [[1,2,3],[4,5,6],[7,8,9]] = > [[1,4,7],[2,5,8],[3,6,9]] ,不做解釋了偷仿,心累哩簿。
  • concat將一組list鏈接為一個list。concat ["123","456"]輸出"123456"
  • and and取一組Bool的list酝静,只有所有的元素都為True才會返回True节榜,否則返回False.and $ map (>4) [5,6,7,8]
  • orand類似,不過是or邏輯
  • anyall表示取list判斷是否有一個符合或者都符合如:any (== 4) [2,3,4,5,6]返回True,all (>4) [5,6,7,8]返回True.
  • splitAt分割list别智,在指定位置斷開splitAt 3 "heyman"返回("hey","man")
  • takeWhile從list中取元素宗苍,一旦遇到不符合條件的元素就停止:takeWhile (>3) [6,5,4,3,2,1,2,3,4,5,4,3,2,1] 返回[6,5,4]
  • dropWhiletakeWhile相似,不過符合條件就刪掉薄榛,直到遇到不符合條件的讳窟。dropWhile (/=' ') "This is a sentence" 返回 " is a sentence"
  • spantakeWhile相似,不過其返回兩個list,第一個list是takeWhile返回的list辰晕,第二個list是剩余的list.
  • sort排序一個list
  • group取一個list作參數(shù)刃永,將其中相鄰并相等的元素各自歸類。
  • isPrefixOfisSuffixOf檢查一個List是否以另一個List開頭或結(jié)尾碌上。
  • partition取一個限制條件和List作為參數(shù),返回兩個List,第一個List包含所有符合條件的元素浦徊,第二個List包含剩余的元素馏予。
  • find接受一個函數(shù)和List,返回第一個符合條件的元素盔性,這個元素是個Maybe值霞丧。返回如下面的形式Just 5Nothing
  • findIndexfind相似冕香,不過返回的是Maybe的索引蛹尝。
  • elemIndexelem相似后豫,返回的是Maybe的索引。
  • lines傳入一個字符串突那,將字符串分行:lines "first line\nsecond line\nthird line" 返回 ["first line","second line","third line"]
  • unlineslines的反函數(shù)
  • wordsunwords可以把一個字符串分成一組單詞或運(yùn)行相反的操作挫酿。
  • nub將一個List中的重復(fù)元素全部篩掉。
  • delete 刪除List中第一個出現(xiàn)的某元素:delete 'h' "hey there"返回 "ey there"愕难,delete 'h'. delete 'h' "hey there"返回"ey tere"
  • \\差集早龟,左集扣除右集合后的集合
  • union并集
  • intersection交集
  • insert將某個元素插入的可排序的List中,插入到首個大于等于該值的前邊

有個函數(shù)叫on函數(shù)猫缭,其定義如下:

on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
f `on` g  \x y -> f (g x) (g y)

即把g函數(shù)的結(jié)果傳遞給f函數(shù)葱弟,所以(==) `on` (>0)就表示函數(shù)\x y -> x>0 == y >0 ,同理 compare `on` length就表示\x y -> length x `compare` length y

Data.Char

處理字符串的模塊

  • isControl 判斷一個字符是否是控制字符。
  • isSpace 判斷一個字符是否是空格字符猜丹,包括空格芝加,tab,換行符等. -
  • isLower判斷一個字符是否為小寫.
  • isUper 判斷一個字符是否為大寫射窒。
  • isAlpha 判斷一個字符是否為字母.
  • isAlphaNum 判斷一個字符是否為字母或數(shù)字.
  • isPrint 判斷一個字符是否是可打印的.
  • isDigit 判斷一個字符是否為數(shù)字.
  • isOctDigit 判斷一個字符是否為八進(jìn)制數(shù)字.
  • isHexDigit 判斷一個字符是否為十六進(jìn)制數(shù)字.
  • isLetter 判斷一個字符是否為字母.
  • isMark 判斷是否為 unicode注音字符藏杖,你如果是法國人就會經(jīng)常用到的. - isNumber 判斷一個字符是否為數(shù)字.
  • isPunctuation 判斷一個字符是否為標(biāo)點(diǎn)符號.
  • isSymbol判斷一個字符是否為貨幣符號.
  • isSeperater 判斷一個字符是否為 unicode 空格或分隔符.
  • isAscii 判斷一個字符是否在 unicode 字母表的前 128 位。
  • isLatin1 判斷一個字符是否在 unicode 字母表的前 256 位.
  • isAsciiUpper 判斷一個字符是否為大寫的 ascii 字符.
  • isAsciiLower 判斷一個字符是否為小寫的 ascii 字符.
  • ord將字符轉(zhuǎn)換為數(shù)字 chr將數(shù)字轉(zhuǎn)換為字符
    可以通過ordchr來實(shí)現(xiàn)一個字符串平移的函數(shù)如:
import Data.Char
encode :: Int -> String -> String
encode salt msg = let msgs = map ord msg
        |       |     digs = map (+ salt) msgs
        |       |     in map chr digs
decode :: Int -> String -> String
decode salt msg = encode (-salt) msg

則輸入encode 1 "abc"輸出"bcd"脉顿,同樣decode 1 "bcd"輸出"abc"

Data.Map

Map是key不重復(fù)一種KV鍵值對集合(List)制市。

  • fromList取一個關(guān)聯(lián)列表,返回一個與之等價的Map 弊予,eg:fromList [("a",1),("b",2)]
  • toListfromList的反函數(shù)
  • insert插入一個新的KV,如:Map.insrt 3 100 Map.empty
  • size返回Map的大小
  • singleton返回只有一個KV的Map:Map.singleton 3 100
  • lookup查詢對應(yīng)的鍵值
  • member某個鍵是否存在一個Map中祥楣,返回Bool:Map.member 3 $ Map.fromList [(2,5),(4,5)]
  • mapMap中的map操作的是V值:Map.map (*100) $ Map.fromList [(1,1),(2,4),(3,9)]返回fromList [(1,100),(2,400),(3,900)]
  • filter同樣操作的是V值。
  • keys返回一個由K組成的List
  • elems返回一個由V組成的List

Data.Set

Set中的數(shù)據(jù)是唯一的汉柒,元素是必須可排序的误褪。注意其因?yàn)楹虳ata.List很多的函數(shù)重復(fù),所以導(dǎo)入的時候使用import qulified Data.Set as Set的方式碾褂。創(chuàng)建一個Set是通過fromList函數(shù)兽间,其他函數(shù)就不列舉了。

創(chuàng)建自己的模塊

在根目錄創(chuàng)建geometry.hs正塌。

module Geometry
( sphereVolume,
sphereArea
) where

sphereVolume :: Float -> Float
sphereVolume radius = (4.0 / 3.0) * pi * (radius ^ 3)

sphereArea :: Float -> Float
sphereArea radius = 4 * pi * (radius ^ 2)

分級模塊創(chuàng)建方式,創(chuàng)建geometry/sphere.hs
則在sphere.hs中的內(nèi)容為:

module Geometry.Sphere
( volume嘀略,
area
) where

volume :: Float -> Float
volume radius = (4.0 / 3.0) * pi * (radius ^ 3)

area :: Float -> Float
area radius = 4 * pi * (radius ^ 2)

構(gòu)造自己的Types和Typeclasses

Bool在標(biāo)準(zhǔn)函數(shù)庫的定義為:

data Bool = False | True

構(gòu)造自己的類型,一種方法就是使用data關(guān)鍵字乓诽。
如構(gòu)造一個圖形帜羊,該圖形可以是Circle也可以是Rectangle,定義為:

data Shape = Circle Float Float Float | Rectangle Float Float Float Float

上述Circle表示圓形鸠天,后邊的跟著的三個Float表示一個Circle由三個Float組成讼育。
創(chuàng)建完Shape后會自動生成Circle及Rectangle類型,此時查看其類型聲明為:

ghci> :t Circle
Circle::Float -> Float -> Float -> Shape

所以Circle 50 50就是返回一個Shape類型,到這就明白了奶段,并不是先有Shape后有Circle饥瓷,而是現(xiàn)有Circle后有了Shape
同樣類型:data Maybe a = Nothing | Just aNothing并不是事先定義的,并且Nothing是一個值構(gòu)造子痹籍,也不是類型呢铆。要創(chuàng)建個某個類型的值,必須使用后邊的值構(gòu)造子蹲缠,即你不能使用Shape關(guān)鍵字來創(chuàng)建一個Shape類型刺洒。
構(gòu)造的方式為:

surface :: Shape -> Float
surface (Circle _ _ r) = pi * r ^ 2
surface (Rectangle x1 y1 x2 y2) = (abs $ x2 - x1) * (abs $ y2 - y1)

注意看傳入的Circle和Rectangele的方式不一樣。
那么調(diào)用方式為:

ghci> surface $ Circle 10 20 30
ghci> surface $ Rectange 0 0 100 100

但是下面的調(diào)用時錯誤的

ghci>Circle 10 20 30 

因?yàn)?code>Circle 10 20 30并不是Show類型吼砂,只有Show類型的數(shù)據(jù)才能被顯示。
那么修改構(gòu)造的方式為:

data Shape = Circle Float Float Float | Rectangle Float Float Float Float deriving (Show)

即在定義的后邊加上deriving (Show).然后再調(diào)用就可以顯示出來了鼎文。

還可以這么定義:

data Point = Point Float Float deriving (Show)
data Shape = Circle Point Float | Rectangle Point Point deriving (Show)

這樣在定義函數(shù)的時候就應(yīng)該是:

surface::Shape -> Float
surface (Circle _ r) = pi * r ^ 2
surface (Rectangle (Point x1 y1) (Point x2 y2))  = (abs $ x2 - x1) * (abs $ y2 - y1)

那么調(diào)用就變?yōu)?

surface (Rectangle (Point 0 0) (Point 100 200))

前邊知道了如何導(dǎo)出函數(shù)的模塊渔肩,那么這種數(shù)據(jù)定義的模塊應(yīng)該怎么導(dǎo)入呢,方式如下:

module Shapes
( Point(..)
, Shape(..)
, surface
, nudge
, baseCircle
, baseRect
) where

即數(shù)據(jù)定義使用Shape(..)的方式拇惋。..表示將Shape中的CircleRectangle類型都導(dǎo)出周偎,這樣外部就可以使用到這兩個構(gòu)造子,如果只導(dǎo)出Circle則需要寫為Shape(Circle)

Record Syntax

定義一個人的名字撑帖,并且生成各種函數(shù):

data Person = Person {firstName :: String,
lastName::String,
age :: Int,
phoneNumber::String
} deriving (Show)  

這樣就生成了Person類型以及firstName``lastName..等等的函數(shù).
創(chuàng)建的時候則為

> Person {firstName="q",lastName="xg",age=10,phoneNumber="123456"}

并且打印Person的時候蓉坎,會將firstName等等顯示出來。

Type parameters

類型參數(shù),類似泛型胡嘿,如:

data Maybe a = Nothing | Just a

如果傳給Maybe的是Char,他就是Maybe Char類型蛉艾。如:Maybe 'a'就是Maybe Char類型的。
Nothing也是Maybe a類型衷敌,所以可以是Maybe Int,也可以是Maybe String
再看一個例子:

data Vector a = Vector a a a deriving (Show)
vplus :: (Num t) => Vector t -> Vector t -> Vector t
...

Derived instances

上邊有涉及到Eq,Ord,Num的typeclass等等類似Java interface的東東勿侯,比如Int屬于Num,但是如何實(shí)現(xiàn)這些interface呢缴罗,用java表達(dá)就是如何implement助琐,方法就是通過派生deriving,使用data創(chuàng)建類型的時候面氓,后續(xù)跟上deriving Num就表明該類型屬于Num兵钮,并自動加上對應(yīng)的行為。
但是注意data創(chuàng)建的是instance舌界,而不是typeclass掘譬,且等號左邊是類型構(gòu)造子,等號右邊是值構(gòu)造子呻拌,一般在創(chuàng)建某種類型的值時屁药,使用的是值構(gòu)造子,而類型構(gòu)造子則用于函數(shù)聲明等位置處,類型構(gòu)造子無法來創(chuàng)建某一個值酿箭,一定要注意复亏,比如下邊創(chuàng)建了一個Person的類型,那么要創(chuàng)建一個Person類型的值缭嫡,就不能使用Person創(chuàng)建缔御,而是要使用等號右邊的關(guān)鍵字(值構(gòu)造子)創(chuàng)建.
只要派生為Eq類,那么定義的數(shù)據(jù)類型就有可比性:

data Person = Person{firstName :: String,lastName :: String,age::Int} deriving Eq

haskell會檢查值構(gòu)造子是否一致妇蛀,再用==檢查其中的所有數(shù)據(jù)(必須都是Eq的成員)是否一致耕突。
同樣也可以指定為多個類型:

data Person = Person{firstName :: String,lastName :: String,age::Int} deriving (Show,Eq,Read)

若將一個類型指定派生為某個A類型,則該類型的所有參數(shù)必須都屬于A類型评架,才可以進(jìn)行派生眷茁。
舉一個經(jīng)常會遇到的例子:

data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday 
        deriving (Eq, Ord, Show, Read, Bounded, Enum)

每個值構(gòu)造子都沒有參數(shù),因?yàn)榕缮薊q,所以具有可比性纵诞,派生了Ord上祈,所以可以比較大小,有Bounded浙芙,可以使用minBound獲取下界登刺,有了Enum,可以使用succ等函數(shù)進(jìn)行枚舉嗡呼。

在data聲明中纸俭,=左邊是類型構(gòu)造子,=右邊用|分割的是值構(gòu)造子,要注意區(qū)分南窗。因?yàn)楹瘮?shù)生命中只能填寫類型揍很,如果分不清楚,可能就將值構(gòu)造子填入万伤,導(dǎo)致死都不知道怎么死的女轿。
注:不要在data中添加類型約束,即便看起來沒問題壕翩。

Type synonyms

給某個類型提供別名:

type String = [Char]

又如:

type PhoneNumber = Stirng
type Name = String
type PhoneBook = [(String,String)]

那么定義某個函數(shù)則為:

inPhoneBook::Name -> PhoneNumber -> PhoneBook -> Bool

而如果不定義別名蛉迹,則該函數(shù)為:

inPhoneBook::String -> String -> [(String,String)] -> Bool

定義別名也可以有參數(shù):

type AssocList k v = [(k,v)]

類型別名也可以定義不全的類型構(gòu)造子.
類型別名一般可以用于函數(shù)類型聲明或類型注釋上,如果要創(chuàng)建一個新類型放妈,不能用類型名+參數(shù)的方式去創(chuàng)建北救,一定要明白類型構(gòu)造子和值構(gòu)造子的區(qū)別。
類似函數(shù)在定義的時候進(jìn)行聲明芜抒,變量在定義的時候也可以進(jìn)行聲明珍策,所以將來::看成通用的一類看待會更好理解這門語言。

a::Int
a=1

是可以工作的宅倒,同樣函數(shù)在定義的時候也是這種格式:

test::a->a
test a = a+1

只不過這個地方用到了泛型攘宙,你也可以這樣聲明

test :: Int -> Int
test a=a+1

遞歸地定義數(shù)據(jù)結(jié)構(gòu)

如List [1]1:[]的語法糖,[1,2]1:2:[][1,2,3]1:2:3:[]
可以看到List的類型類似這樣x:listx其中x是匹配到的第一個元素蹭劈,而listx是一個匹配的新的list,每個List類型都可以匹配為x:listx類型疗绣,并且每個listx都可以匹配為x:listx類型,除了基礎(chǔ)類型[],所以在定義List的時候就可以通過遞歸來進(jìn)行定義铺韧,如將[]替換為empty,:替換為Cons,那么定義List就可以是這樣:
data List a = Empty | Cons a List a deriving (Show ,Read,Eq,Ord)
那么在創(chuàng)建List的時候就可以這樣,1 `Cons` Empty注意最后的Empty就已經(jīng)表明其是一個List,所以創(chuàng)建的遞歸數(shù)據(jù)類型一般都是需要最基礎(chǔ)的類型多矮,比如Empty,比如原始List的[]等哈打。
Cons也可以替換為一個符號塔逃,如

data List a = Empty | a :-: (List a) deriving (Show, Read,Eq, Ord)

那么如果要定義該符號的優(yōu)先級,需要加上infixr關(guān)鍵字料仗,其后的數(shù)字表示優(yōu)先級:

infixr 5 :-:
data List a = Empty | a :-: List a deriving (Show, Read,Eq, Ord)

那么如果要做模式匹配的話湾盗,是可以這樣做的:

a:-:b = xxxx

原因就是:-:是構(gòu)造子,而模式匹配就是通過構(gòu)造子來進(jìn)行匹配的立轧,同理[]也是構(gòu)造子,:也是構(gòu)造子格粪,所以你可以通過x:xs來進(jìn)行匹配,注意模式匹配中匹配的都是值構(gòu)造子肺孵,一定不要寫類型。
二叉樹

data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show,Read,Eq)

創(chuàng)建節(jié)點(diǎn)

singleton:: a -> Tree a
singleton x = Node x EmptyTree EmptyTree

插入節(jié)點(diǎn)

treeInsert::(Ord a)=> a->Tree a->Tree a
treeInesrt x EmptyTree = singleton x
treeInsert x (Node a left right)
        | x == a = Node x left right
        | x < a = Node a (treeInsert x left) right
        | x> a = Node a left (treeInsert x right)

查找節(jié)點(diǎn)

treeElem :: (Ord a) => a -> Tree a -> Bool
treeElem x EmptyTree = False  
treeElem x (Node a left right)
    | x == a = True
    | x < a = treeElem left
    | x > a = treeElem right
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末颜阐,一起剝皮案震驚了整個濱河市平窘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌凳怨,老刑警劉巖瑰艘,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異肤舞,居然都是意外死亡紫新,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門李剖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來芒率,“玉大人,你說我怎么就攤上這事篙顺∨忌郑” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵德玫,是天一觀的道長匪蟀。 經(jīng)常有香客問我,道長宰僧,這世上最難降的妖魔是什么材彪? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上段化,老公的妹妹穿的比我還像新娘嘁捷。我一直安慰自己,他們只是感情好穗泵,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布普气。 她就那樣靜靜地躺著,像睡著了一般佃延。 火紅的嫁衣襯著肌膚如雪现诀。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天履肃,我揣著相機(jī)與錄音仔沿,去河邊找鬼。 笑死尺棋,一個胖子當(dāng)著我的面吹牛封锉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播膘螟,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼成福,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了荆残?” 一聲冷哼從身側(cè)響起奴艾,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎内斯,沒想到半個月后蕴潦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡俘闯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年潭苞,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片真朗。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡此疹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出遮婶,到底是詐尸還是另有隱情秀菱,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布蹭睡,位于F島的核電站衍菱,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏肩豁。R本人自食惡果不足惜脊串,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一辫呻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧琼锋,春花似錦放闺、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至谜叹,卻和暖如春匾寝,著一層夾襖步出監(jiān)牢的瞬間荷腊,已是汗流浹背猜年。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工乔外, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留杨幼,地道東北人补疑。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像撵孤,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子闭专,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容

  • //Clojure入門教程: Clojure – Functional Programming for the J...
    葡萄喃喃囈語閱讀 3,658評論 0 7
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法奈虾,類相關(guān)的語法肉微,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法厚者,線程的語...
    子非魚_t_閱讀 31,622評論 18 399
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理志膀,服務(wù)發(fā)現(xiàn),斷路器馆蠕,智...
    卡卡羅2017閱讀 134,651評論 18 139
  • 第5章 引用類型(返回首頁) 本章內(nèi)容 使用對象 創(chuàng)建并操作數(shù)組 理解基本的JavaScript類型 使用基本類型...
    大學(xué)一百閱讀 3,233評論 0 4
  • 看著路旁綠油油的田野,路旁的樹寺酪,一直在后退,我要回家了。 謝謝閱讀膜蛔。非常感謝你命黔,感謝你看著我成長。
    我心我愿秀閱讀 146評論 0 4