Haskell 笔记1
April 12, 2017
这是一些阅读Learn You a Haskell for Great Good!的时候的笔记,之前用Latex写的,放在Dropbox里面,现在想把它们整理一下,放在博客里。这是第二章 Starting Out的笔记。
Baby’s first functions
2017/03/15 10:32:18
function call的优先级最高。
定义函数这样:
doubleMe x = x * 2
和SML基本一样。
doubleUs x y = doubleMe x + doubleMe y
doubleUs1(x, y) = doubleMe x + doubleMe y
这两个的区别?
第一个是curried function,第二个函数接收一个参数,这个参数是一个含有两个元素的tuple,也叫做pair。
函数调用格式:
doubleUs 3 4
doubleUs1(3, 4)
if else
格式:
if x > 50 then x else x * 3
和SML基本一样。同样,else部分不能省略。因为这是一个「表达式」,表达式必须有一个值,所以,不管条件是否满足,一定要有一部分被求值,并且当作这个表达式的值。并且,这两部分的表达式的返回值的类型必须一样。
不能在ghci里面像在文件里面那样定义函数。有几种解决方法:defun function in ghci
'
符号在函数和变量名字里面是合法的。比如doubleMe'
是一个合法的函数名字
函数名字不能以大写字母开头。没有参数的函数名字其实就是一个「绑定」,不可修改,类似于常量吧?
An intro to lists
string也是一种list
一个单词:「homogenous」,同质的。list是一种「同质」的数据结构,也就是说,列表可以只含有很多个int,也可以只含有很多个string,但是不能同时含有int和string。这一点也和SML基本一样。
在程序里面写a = 1
和在ghci里面let a = 1
等价。
string其实一种语法糖:"he"
等价于['h', 'e']
合并列表运算符:++
let xs = [1, 2]
let ys = [3, 4]
xs ++ ys
结果是一个新建的列表,注意:内部实现会把左边的列表遍历一遍。所以:类似于这样的操作[1, 2, 3] ++ [4]
的效率可能非常低。
:
运算符类似于lisp中的cons
,3 : [1, 3]
类似于(cons 3 '(1 3))
所以:[1, 2, 3]就等价于:1 : 2 : 3 : []
,类似于:(cons 1 (cons 2 (cons 3 null)))
取得下标为1的元素:[1, 2, 3] !! 1
,下标从0开始计数
列表里面的子列表(和子列表的子列表等)所含元素必须一致。
如果列表中的元素可以比较大小,那么列表也可以比较大小。按照字典序比较。(这里的字典序并不是我以前认为的字符串的字典序。。。比如:[10000] > [99]是True)
取得列表头元素:
head [1, 2, 3]
类似于lisp中的
(car '(1 2 3))
同样地,
tail [1, 2, 3] == [2, 3]
类似于:
(cdr '(1 2 3))
取得最后一个元素:
last [1, 2, 3] == 3
取得除最后一个元素之外的元素:
init [1, 2, 3] == [1, 2]
从空列表中取头元素会抛异常:(lisp里面的car并不会)
head []
取得列表长度:
length [1, 2, 3]
类似于lisp中:
(length '(1 2 3))
检查一个列表是否为空:
null [1, 2, 3]
null []
类似于lisp中:
(null '(1 2 3))
(null nil) (null '())
把一个列表倒序:
reverse [1, 2, 3]
从一个列表中取得开头的k个元素,返回取得的元素列表:
take k [1, 2, 3]
当k == 0
时,返回[]
当k > length xs
时,返回xs
去掉列表开头的k个元素,返回剩余元素的列表:
drop 2 [1, 2, 3, 4, 5] == [3, 4, 5]
drop 0 [1, 2] == [1, 2]
drop 10000 [1, 2] == []
取得最大、最小元素:
maximum [1, 2, 3] == 3
minimum [1, 2, 3] == 1
求列表所有元素的和、积:
sum [1, 2, 3] = 6
product [1, 2, 3, 4] = 24
判断一个元素是否在列表里面:
4 `elem` [1, 2, 3, 4] == True
elem 4 [1, 2, 3, 4] == True
Texas ranges
ranges:[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"
[2, 4..20] == [2,4,6,8,10,12,14,16,18,20]
[3, 6..20] == [3,6,9,12,15,18]
注意:[20..1]
并不合法,必须这样写:[20, 19..1]
当浮点数字出现的时候,[0.3, 0.6..1]
得到:[0.3,0.6,0.8999999999999999]
,因为浮点数不精确。
如果去掉上限,会构造出一个无限列表:[13, 26..]
,这里haskell会「懒惰求值」,仅仅当列表中的元素被用到的时候才会生成。可以这样用:take 24 [13, 26..]
、take 3 [10..]
生成循环列表:
cycle [1, 2, 3]
比如:
take 10 (cycle [1, 2, 3]) == [1,2,3,1,2,3,1,2,3,1]
生成某个元素的无限列表:
repeat 5
比如:
take 10 (repeat 5) == [5,5,5,5,5,5,5,5,5,5]
I’m a list comprehension
列表解析:
[x * 2 | x <- [1..5]] == [2,4,6,8,10]
[x * 3 | x <- [1..5], x * 3 > 10] == [12, 15]
可以在binding后面追加几个predicate,用逗号分割。比如:一个函数,接受一个整数列表,把所有小于10的奇数替换成”BOOM!“,所有大于等于10的奇数替换成”BANG!“,所有的偶数舍弃:
boomBangs xs = [if x < 10 then "BOOM!" else "BANG!" | x <- xs, odd x]
当binding有多个的时候,效果相当于其它语言里面的嵌套的 for循环:
[x * y | x <- [2, 5, 10], y <- [8, 10, 11]] ==
[16,20,22,40,50,55,80,100,110]
利用列表解析重新定义length函数:
length' xs = sum [1 | _ <- xs]
这里_
起到一个占位符的作用。
Tuples
果然有tuple这个类型。它和list的区别是:一个tuple的类型和它包含的元素的个数和元素类型都有关系,比如包含一个int元素的tuple和包含两个int元素的tuple类型不一样。
一个list的类型仅仅和它包含的元素有关系,一个int list可以包含任意多个元素,它们的类型都属于int list。
另外,tuple不是homogenous的,也就是,一个tuple里面可以包含int,同时也可以包含string等等。
比如[(1, 2), (3, 4), (5, 6)]是合法的,然而[(1, 2), (3, 4, 5), (5, 6)]是非法的,因为包含三个元素的tuple和包含两个元素的tuple类型不一样,同时list是homogenous的。
所以,不存在这样一个函数:它接受一个任意的tuple,然后追加一个元素到tuple里。必须这样:针对不同长度的tuple添加不同的函数。
长度为2的tuple也叫做pair,tuple的长度不能小于2。相同类型(长度相同,元素类型相同)的tuple可以相互比较。不同长度的list可以相互比较。
取得一个pair的第一、二个元素:(注意是pair,并不是tuple)
fst (8, 11) == 8
snd (1, 2) == 2
zip函数,接受两个列表作为参数,把对应位置的两个元素组成一个pair,返回一个pair list
zip [1, 2, 3] [4, 5, 6] == [(1,4),(2,5),(3,6)]
如果这两个列表长度不同,那么最后的结果列表和较短的列表长度一致。因为懒惰求值,zip的一个参数可以是无限列表。
例子:求三边长小于10并且周长为24的所有的三角形。
triangles = [(a, b, c) | c <- [1..10], b <- [1..c], a <- [1..b],
a + b + c == 24, a ^ 2 + b ^ 2 == c ^ 2]