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中的cons3 : [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]
comments powered by Disqus