趣学指南 📅
List:
序列的基本操作
let list = [1,2,3]
head list -> [1]; head [] -> Exception
tail list -> [2,3]; tail [] -> Exception
init list -> [1,2]; init [] -> Exception
take 1 list -> [1]; take 0 list -> []; take 100 list -> list
null list -> False; null [] -> True
reverse list -> [3,2,1]; reverse [] -> []
drop 1 list -> [2,3]; drop 100 list -> []; drop 0 list -> list
minimum list -> 1; minimum [] -> Exception
maximum list -> 3; maximum [] -> Exception
sum list -> 6; sum [] -> 0;
sum “hello haskell” -> <interactive>:60:1: error:
• No instance for (Num Char) arising from a use of ‘sum’
• In the expression: sum "hello haskell"
In an equation for ‘it’: it = sum "hello haskell"
product list -> 6; product [] -> 1
使用区间
range 是构造List的方法之一,而其中的值必须是可枚举的
Ex1: 构造1到20的自然数list
Prelude> let o = [1..20]
Prelude> print o
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
Ex2: 1到20之间的偶数
Prelude> print [2,4..20]
[2,4,6,8,10,12,14,16,18,20]
Ex3: 20到1
Prelude> [20..1]
[]
Prelude> [20,19..1]
[20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1]
如果不标明上限将会得到一个无限长度的序列,但由于Haskell是 惰性的 ,它不会对无限长度的list求值,它会等待你从list中取值
不是这种惰性......
Ex4: 取前24个13的倍数
[13, 26..] -> 无限长度的序列
取出前24个 take 24 [13, 26..]
Prelude> take 24 [13,26..]
[13,26,39,52,65,78,91,104,117,130,143,156,169,182,195,208,221,234,247,260,273,286,299,312]
Ex5: cycle -> 接受一个list, 返回一个无限长度的list
Prelude> take 10 (cycle [1])
[1,1,1,1,1,1,1,1,1,1]
Prelude> take 10 (cycle [1,2])
[1,2,1,2,1,2,1,2,1,2]
Ex6: repeat -> 接收一个值,返回一个该值的无限序列
Prelude> take 10 (repeat "")
["","","","","","","","","",""]
Prelude> take 5 (repeat 5)
[5,5,5,5,5]
Ex7: replicate: replicate 3 10 => [10, 10, 10]
Prelude> replicate 3 10
[10,10,10]
List Comprehension
序列集合 => 从序列中,通过某些条件筛选出的list
Ex1: 简单示例,
Prelude> [x*2 | x <- [1..10]]
[2,4,6,8,10,12,14,16,18,20]
Ex2: 添加筛选谓词
Prelude> [x*2 | x <- [1..10], x*2 >= 12, x / 3 == 0]
[]
Prelude> [x*2 | x <- [1..10], x*2 >= 12, x `mod` 3 == 0]
[12,18]
Ex3: 更加复杂的例子: 将输入的序列中小于10的奇数变为”BOOM!”, 大于10的奇数变为”BANG!”
Prelude> boombangs xs = [if x < 10 then "BOOM!" else "BANG!" | x <- xs, odd x]
Prelude> boombangs [7..17]
["BOOM!","BOOM!","BANG!","BANG!","BANG!","BANG!"]
Ex4: 编写我们自己的length函数
Prelude> length' xs = sum [1 | _ <- xs]
Prelude> length' []
0
Prelude> length' [1]
1
Prelude> length' [[1], [2]]
2
Ex5: 字符串也是list,同样适用; 过滤掉字符串中除了大写字母外的其它字符
Prelude> removeNonUppercase st = [c | c <- st, c `elem` ['A'..'Z']]
Prelude> removeNonUppercase "Hello World!"
"HW"
Prelude> removeNonUppercase "sakljMjkldsjaAdljsajlCB8312789783OOK aaaaAiiiIR"
"MACBOOKAIR"
Tuple
Tuple 简介: Tuple 和 List 很类似,都可以将多个值存入一个容器内,但是List只能存储 同种类型 的数据,而Tuple 则要求对数据的数目非常的明确。Tuple 使用()来包括内部的值。
- Tuple 可以包含不同类型的值
- Tuple 不可以只有一个值
- 由于Haskell是强类型的,这样的两个tuple 无法放入 list: [(1, 2), (“g”, 10) ]将无法编译通过
- 二元组取值 =>
Prelude> fst (1,2)
1
Prelude> fst ("haskell", 9000)
"haskell"
Prelude> snd (1,2)
2
Prelude> snd ("haskell", 1888)
1888
使用zip函数来生成List
Ex1: zip [1,2,3] [“good”, “morning”, “o”]
Prelude> zip [1,2,3] ["good", "morning", "o"]
[(1,"good"),(2,"morning"),(3,"o")]
Ex2: 在两个list 长度不对应时在最早结束的list处断开 => zip [1,2,3] [“l”, “p”, “ui”, “op”]
Prelude> zip [1,2,3] ["l", "p", "ui", "op"]
[(1,"l"),(2,"p"),(3,"ui")]
Ex3: Haskell是惰性的,使用zip 函数同时处理有限和无限序列同样可以 =>
zip [1..] [“apple”, “orange”, “mango”, “pear”]
Prelude> zip [1..] ["apple", "orange", "mango", "pear"]
[(1,"apple"),(2,"orange"),(3,"mango"),(4,"pear")]
Ex4: 使用元组解决实际问题 =>
取得所有三边长度皆为整数且小于等于10,周长为24的直角三角形
Prelude> let trangles' = [(a,b,c) | c <- [1..10], a <- [1..c], b <- [1..a], a^2 + b^2 == c^2, a + b + c == 24]
Prelude> print trangles'
[(8,6,10)]
Types and Type-classes
Haskell 是 静态类型 语言
=> 在编译时每个表达式的类型已经完全确定。
=> 如果程序中有布尔值与整数相除的动作,将不会通过编译。
Haskell 有类型推导
=> let a = 3, 不用告诉编译器a 是Int类型
使用 :t 在ghci中查询数据类型
Prelude> :t 'a'
'a' :: Char
Prelude> :t "hello"
"hello" :: [Char]
同样,函数也有类型,并且对于函数来说,标明类型将使代码更加清晰
=> 三个数相加:
addThree :: Int -> Int -> Int -> Int ; addThree a b c = a + b + c
Prelude> addTwo :: Int -> Int -> Int; addTwo a b = a + b
Prelude> addTwo 99 1
100
常见数据类型
=> Int : 整数,有上限和下限
=> Integer: 整数,无上下限,只和当前机器存储有关
=> Float: 单精度浮点
=> Double: 双精度浮点
=> Bool
=> Char
=> Tuple
Type Variables 泛型
head 函数的类型
=>
Prelude> :t head
head :: [a] -> a
类型名都是大写的,a并没有大写,所以a不会是一个类型。a是一个类型变数,意味着a可以是任意类别,和泛型非常类似。
使用类别变数构造的函数称为’多态函数’,这样我们可以编写出更多与类型无关的函数
=>
Prelude> :t fst
fst :: (a, b) -> a
Typeclasses
如果某一个类型属于某一种Typeclass,则这个类型必然实现了该Typeclass所描述的行为。和OC中的协议非常类似
=> ‘==‘函数的类型
Prelude> :t (==)
(==) :: Eq a => a -> a -> Bool
Eq即为a的Typeclass, 调用该函数的类型一定是遵守了Eq所描述的行为的
常用的Typeclasses
=> Eq: 可判断相等性的类型。除函数外的所有类型都实现了Eq,都可以比较大小
=> Ord: 包含可比较大小的类型。Ord包中包含了 > < >= <= 之类的用于比较大小的函数。compare函数比较两种相同类型的值,返回结果为GT(greater than) LT(less than) EQ(equal)
Prelude> 5 `compare` 3
GT
Prelude> "A" `compare` "Z"
LT
=> Show: 包含可用字符串表示的类型。
Prelude> show True
"True"
Prelude> show [1,2,3]
"[1,2,3]"
// 函数无法show
Prelude> add8 :: Int -> Int; add8 a = a + 8
Prelude> show add8
<interactive>:159:1: error:
• No instance for (Show (Int -> Int)) arising from a use of ‘show’
(maybe you haven't applied a function to enough arguments?)
• In the expression: show add8
In an equation for ‘it’: it = show add8
=> Read: 和Show相反,表示可以从字符串转换成某种类型的Typeclass。
Prelude> read "[1,2,3,4]" ++ [5]
[1,2,3,4,5]
// 无法直接推断出想要转换的类型
Prelude> read "4"
*** Exception: Prelude.read: no parse
// 可以直接指定转换的类型
Prelude> read "4" :: Int
4
=> Enum: 可枚举类型,“該 Typeclass 包含的型別有:(), Bool, Char, Ordering, Int, Integer, Float 和 Double”
=> Bounded: 有上下限的数据,使用maxBound和minBound输出上下限
Prelude> maxBound :: Bool
True
Prelude> minBound :: Int
-9223372036854775808
=> Num: 表示数字的Typleclass,它的成员都有数字的特征。
Prelude> :t (*)
(*) :: Num a => a -> a -> a
=> Integer: 仅仅代表整数的Typeclass,成员有Int 和Integer
补充
Q1: 类型是干什么用的?
Ans: 在计算机的最底层,处理的都是没有任何附加结构的字节(Byte)。类型系统在这个基础上进行了抽象:为那些单纯的字节加上了意义,使得我们可以说“这些字节是文本”, “那些字节是机票预约数据”等等。
Q2: Haskell 类型系统的特点
Ans:
- 强类型的
- 静态的
- 自动类型推导