02 从零开始

交互式环境中的简单测试

设置命令行提示符: set prompt "ghci>"

四则运算

ghci> 2 + 15
17

ghci> 49 * 100
4900

ghci> 1892 - 1472
420

ghci> 5 / 2
2.5

ghci>

括号改变优先级

ghci> (50 * 100) - 4999
1  

ghci> 50 * 100 - 4999
1  

ghci> 50 * (100 - 4999)
-244950

布尔运算

ghci> True && False
False

ghci> True && True
True

ghci> False || True
True

ghci> not False
True

ghci> not (True && True)
False

相等性判断

ghci> 5 == 5
True

ghci> 1 == 0
False

ghci> 5 /= 5
False

ghci> 5 /= 4
True

ghci> "hello" == "hello"
True

类型错误

传给函数的参数的类型必须符合要求,否则会报错

ghci> 5+"llama"

No instance for (Num [Char])
arising from a use of `+' at :1:0-9
Possible fix: add an instance declaration for (Num [Char])
In the expression: 5 + "llama"
In the definition of `it': it = 5 + "llama"

调用函数

函数调用不需要括号,参数之间用空格分开

ghci> succ 8
9

ghci> min 9 10
9

ghci> min 3.4 3.2
3.2

ghci> max 100 101
101

函数的高优先级

函数是右结合的

ghci> succ 9 + max 5 4 + 1
16

ghci> (succ 9) + (max 5 4) + 1
16

自定义函数

Haskell 中的自定义函数和数学中的函数非常类似,例如 f(x) = x + x

下面定义的函数会将一个数乘以 2

doubleMe x = x + x

保存并载入

保存为 baby.hs 文件,然后用 :l 指令载入

ghci> :l baby  
[1 of 1] Compiling Main             ( baby.hs, interpreted )
Ok, modules loaded: Main.

ghci> doubleMe 9
18

ghci> doubleMe 8.3
16.6

分支表达式 if ... then ... else ...

在 **Haskell **中

  • if 语句是一个表达式,因此是有返回值的
  • 必须有 eles 语句
  • 两个分支返回的数据类型必须相同,否则报错
doubleSmallNumber x = if x > 100
                      then x
                      else  x*2

因为 if 语句是个表达式,最终可以简化成一个值,因此可以用在任何地方

doubleSmallNumber' x = (if x > 100 then x else x*2) + 1

函数的命名规范

  • 函数名中可以使用单引号
  • 函数名首字母必须小写
  • 没有参数(变量)的函数,相当于定义了一个常量,他的值不可修改

List 入门

List 本质上就是个序列,和其他动态语言不同,序列中的项是同一种类型

注意:在交互式环境下,需要用 let 定义变量

ghci> let lostNumbers = [4,8,15,16,23,48]  
ghci> lostNumbers  
[4,8,15,16,23,48]

++ 合并运算

[1,2,3,4] ++ [9,10,11,12]  ==  [1,2,3,4,9,10,11,12]

: 在列表头上添加一个元素,速度很快

'A' : " SMALL CAT"  ==  "A SMALL CAT" 
5:[1,2,3,4,5]  ==  [5,1,2,3,4,5]

[] 空列表

空列表是列表中的最内层元素,列表实际上是一系列列连接运算的语法糖

[1,2,3] ==  1:2:3:[]
  • !! 索引列表中的元素,注意不要超出边界
"Steve Buscemi" !! 6  ==  'B' 
[9.4,33.2,96.2,11.2,23.25]!! 1  ==  33.2

列表中的列表

列表中可以嵌套列表,但是子列表的类型必须相同

ghci> let b = [[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3]]
ghci> b
[[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3]]

ghci> b ++ [[1,1,1,1]]
[[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3],[1,1,1,1]]

ghci> [6,6,6]:b
[[6,6,6],[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3]]

ghci> b !! 2
[1,2,2,3,4]

列表比较大小

列表可以比较大小,比较的时候会对其中的元素依次进行比较

ghci> [3,2,1] > [2,1,0]  
True  

ghci> [3,2,1] > [2,10,100]  
True  

ghci> [3,4,2] > [3,4]  
True  

ghci> [3,4,2] > [2,4]  
True  

ghci> [3,4,2] == [3,4,2]  
True

head 取出列表的头

head [5,4,3,2,1]  ==  5 

tail 取出除了头以外的部分

tail [5,4,3,2,1]   ==  [4,3,2,1] 

last 取出列表的尾

last [5,4,3,2,1]   ==  1 

init 取出除了尾以外的部分

init [5,4,3,2,1]  ==  [5,4,3,2]

注意:上面四个函数处理空列表会出错

ghci> head []  
*** Exception: Prelude.head: empty list

length 返回一个 List 的长度

ghci> length [5,4,3,2,1]  
5

null 检查一个 List 是否为空

ghci> null [1,2,3]  
False  

ghci> null []  
True

reverse 将一个 List 反转

ghci> reverse [5,4,3,2,1]  
[1,2,3,4,5]

take 返回列表的前几个元素

  • 若是取超过 List 长度的元素个数,只能得到原 List
  • take 0 个元素,则会得到一个空 Lis!
ghci> take 3 [5,4,3,2,1]  
[5,4,3]  

ghci> take 1 [3,9,3]  
[3]  

ghci> take 5 [1,2]  
[1,2]  

ghci> take 0 [6,6,6] 
[]

drop 删除列表的前几个元素

ghci> drop 3 [8,4,2,1,5,6]  
[1,5,6]  

ghci> drop 0 [1,2,3,4]  
[1,2,3,4]  

ghci> drop 100 [1,2,3,4]  
[]

maximum 返回列表中最大的元素

minimun 返回列表中最小的元素

ghci> minimum [8,4,2,1,5,6]  
1  
ghci> maximum [1,9,2,3,4]  
9

sum 返回列表 中所有元素的和

product 返回列表中所有元素的积

ghci> sum [5,2,1,6,3,2,5,7]  
31  

ghci> product [6,2,1,2]  
24  

ghci> product [1,2,5,6,7,9,2,0]  
0

elem 判断一个元素是否在列表中

ghci> 4 `elem` [3,4,5,6]  
True  
ghci> 10 `elem` [3,4,5,6]  
False

使用 Range

Range 可以定义一个有序序列

所谓序列类似于数学上的数列,可以递增或者递减

[1..20]  ==  [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20] 
['a'..'z']  ==  "abcdefghijklmnopqrstuvwxyz" 
['K'..'Z']  ==  "KLMNOPQRSTUVWXYZ" 

可以指定序列的间距

[2,4..20]  ==  [2,4,6,8,10,12,14,16,18,20] 
[3,6..20]  ==  [3,6,9,12,15,18] 

不要使用浮点数创建序列,不精确

[0.1, 0.3 .. 1]   ==  [0.1,0.3,0.5,0.7,0.8999999999999999,1.0999999999999999] 

惰性

cycle 创建一个无限循环的序列

take 10 (cycle [1,2,3])  ==  [1,2,3,1,2,3,1,2,3,1] 
take 12 (cycle"LOL ")  ==  "LOL LOL LOL " 

repeat 创建一个无限长度的序列

take 10 (repeat 5)  ==  [5,5,5,5,5,5,5,5,5,5]

列表解析

列表解析本质上就是代数上的集合变换。下面将序列中的元素翻倍

[x*2 | x <- [1..10]]  == [2,4,6,8,10,12,14,16,18,20]

条件过滤

可以给集合设定过滤条件,符合条件的项才会进入新的列表

下面条件只将序列中翻倍后能够大于 12 的元素进行翻倍后进入新的序列

[x*2 | x <- [1..10], x*2 >= 12]  == [12,14,16,18,20]

50100 之间所有除 73 的元素

[ x | x <- [50..100], x `mod` 7 == 3]  == [52,59,66,73,80,87,94]

注意:条件由逗号分隔

多条件过滤

可以设置多个过滤条件

[ x | x <- [10..20], x /= 13, x /= 15, x /= 19]  == [10,11,12,14,16,17,18,20]

多个列表解析

将多个序列变换成一个序列,每个 <- 符号处理一个序列

  • 注意:过滤条件可以使用多个解析变量进行运算
  • 将两个变量的运算结果进入新序列
[ x*y | x <-[2,5,10], y <- [8,10,11], x*y > 50]  == [55,80,100,110]

全排列

解析多个序列的时候是一种全排列组合

ghci> let nouns = ["hobo","frog","pope"]
ghci> let adjectives = ["lazy","grouchy","scheming"]

ghci> [adjective ++ " " ++ noun | adjective <- adjectives, noun <- nouns]
["lazy hobo","lazy frog","lazy pope","grouchy hobo","grouchy frog", "grouchy pope","scheming hobo",
"scheming frog","scheming pope"]

用列表解析实现一个 length 函数

如果不关心列表中的元素是什么,可以用下划线代替

length' xs = sum [1 | _ <- xs]

过滤大写字母

就是用条件过滤将大写字母过滤出来

注意:这里使用了 \elem` ` 函数

removeNonUppercase st = [ c | c <- st, c `elem` ['A'..'Z']]

ghci> removeNonUppercase "Hahaha! Ahahaha!"
"HA"

ghci> removeNonUppercase "IdontLIKEFROGS"
"ILIKEFROGS"

嵌套列表解析

解析出来的项如果是个列表,可以再次解析

ghci> 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]]

ghci> [ [ x | x <- xs, even x ] | xs <- xxs]
[[2,2,4],[2,4,6,8],[2,4,2,6,2,6]]

元组

列表像是一个数组,而元组更像是向量,其中每个元素都是一个分量

元组有自己的数据类型,分量的数目和类型决定了元组的类型。向下面这样的列表是错误的,因为其中的元组类型不一样

[(1,2),(8,11,5),(4,5)]

Couldn't match expected type `(t, t1)'
against inferred type `(t2, t3, t4)'
In the expression: (8, 11, 5)
In the expression: [(1, 2), (8, 11, 5), (4, 5)]
In the definition of `it': it = [(1, 2), (8, 11, 5), (4, 5)]

元组比较大小

Prelude> (1,2,3)>(2,3,4)
False

没有一元组

最短的元组也应该有两个分量,叫做二元组或者序对。

一元组没有意义,就是数据本身

Prelude> (2)
2

Prelude> (1,2)
(1,2)

取二元组的分量

  • fst 取出序对中的第一个分量
  • snd 取出序对中的第二个分量

注意:fst snd 这两个函数只能处理二元组,不能处理多元组

ghci> fst (8,11)
8

ghci> fst ("Wow", False)
"Wow"

ghci> snd (8,11)
11

ghci> snd ("Wow", False)
False

zip 函数

zip 函数可以将两个列表中的元素组合起来,变成一个二元组列表

注意:如果用列表解析处理,就变成了全排列,或许可以用 map ?

ghci> zip [1,2,3,4,5] [5,5,5,5,5]
[(1,5),(2,5),(3,5),(4,5),(5,5)]

ghci> zip [1 .. 5] ["one", "two", "three", "four", "five"]
[(1,"one"),(2,"two"),(3,"three"),(4,"four"),(5,"five")]

不同长度的列表也能组合,以较短的为标准

ghci> zip [1..] ["apple", "orange", "cherry", "mango"]
[(1,"apple"),(2,"orange"),(3,"cherry"),(4,"mango")]

通过列表解析构造元组的列表

问题:找出所有边长都小于等于 10 ,周长等于 24 的直角三角形

注意:这里的解析条件使用了三个解析变量进行运算

ghci> let rightTriangles' = 
        [(a,b,c) |  c <- [1..10], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2, a+b+c == 24]

ghci> rightTriangles'
[(6,8,10)]

你可能感兴趣的:(02 从零开始)