Haskell笔记二

类型

特性

强（strong）类型

静态（static）

可以通过自动推导（automatically inferred）得出

基本类型

Copy
Char
Bool
Int 常用 定长（fixed-width）整数 一般32/64位宽
Integer 不常用 不定长
Double 常用 浮点数
Float 不常用 计算慢

Copy
Prelude> :type 'a'
'a' :: Char

Prelude> 'a' -- 自动推导
'a'

::符号
Prelude> 'a' :: Char -- 显式签名
'a'

调用函数

Copy
Prelude> odd 3
True

Prelude> compare 2 3
LT
e.g. LT EQ GT

Prelude> (compare 2 3) == LT
True

Prelude> compare (sqrt 3) (sqrt 6) -- 此处括号不可去
LT

复合数据类型：列表和元组

不同？

列表可以任意长，且只能包含类型相同的值。

元组的长度是固定的，但可以包含不同类型的值。

可递归性

[[Int]] 是一个包含 [Int] 类型值的列表，而 [Int] 又是一个包含 Int 类型值的列表即用 Int 类型替换了类型变量 a 。

列表 [] 及 head, tail, last ; take, drop

head 取第一个元素， tail 取除第一个外所有
last 取最后一个元素

Copy
Prelude> head [1, 2, 3, 4]
1

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

Prelude> tail "list"
"ist"
ghci> last [1,2,3]
3

Copy
Prelude> take 2 [1, 2, 3, 4, 5]
[1,2]
Prelude> drop 2 [1, 2, 3, 4, 5]
[3,4,5]
Prelude> drop 4 [1, 2]
[]
Prelude> drop (-1) "foo" --第一个参数小于或等于 0时， drop 函数返回整个输入列表
"foo"

元组 () 及 fst, snd

大小至少为 2

元组的类型由它所包含元素的数量、位置和类型决定

Copy
Prelude> :type (True, "hello")
(True, "hello") :: (Bool, [Char])
Prelude> (4, ['a', 'm'], (16, True))
(4,"am",(16,True))
Prelude> :t ()
() :: ()

Copy
Prelude> fst (1, 'a')
1

Prelude> snd (1, 'a')
'a'

元组使用场景 e.g.

如果一个函数需要返回多个值，那么可以将这些值都包装到一个元组中，然后返回元组作为函数的值。

当需要使用定长容器，但又没有必要使用自定义类型的时候，就可以使用元组来对值进行包装。

利用括号将表达式传给函数

a b c d 等于 (((a b) c) d)

纯度

带副作用的函数称为 不纯（impure）函数 ，而将不带副作用的函数称为 纯（pure）函数。

副作用即函数中全局变量可修改如取决于某一时刻的值，本质上是函数的一种不可见的（invisible）输入或输出。Haskell 的函数在默认情况下都是无副作用的：函数的结果只取决于显式传入的参数。

不纯函数的类型签名都以 IO 开头，以下为检测 e.g.：

Copy
Prelude> :type readFile
readFile :: FilePath -> IO String
-- 不纯函数的类型签名都以 IO 开头

加载.hs 文件

ghci 中通过:load

变量

一旦变量绑定了（也即是，关联起）某个表达式，那么这个变量的值就不会改变。如.hs 文件中就不行，但 ghci 命令行里可以。

e.g. [Assign.hs]

条件求值

不同分支之间的类型必须相同。
e.g.:

[myDrop.hs]

Copy
-- file: ch02/myDrop.hs

myDrop :: Int -> [a] -> [a]
myDrop n xs = if n <= 0 || null xs -- ## null用法
then xs -- 缩进不可省略
else myDrop (n - 1) (tail xs) -- 缩进不可省略

-- 代换（substitution）和重写（rewriting）
-- 代换了变量n和xs
-- 重写了myDrop函数

-- ## 惰性求值
-- 也可写成一行 e.g.[myDropX.hs](ch02/myDrop.hs)

-- ## 条件求值
-- null xs not equals to xs == null
-- then 和 else 之后的表达式称为“分支”，
-- 不同分支之间的类型必须相同。
-- 而if不是分支。

-- e.g.
-- Prelude> if True then 1 else "foo"

-- <interactive>:2:14:
-- No instance for (Num [Char])
-- arising from the literal `1'
-- Possible fix: add an instance declaration for (Num [Char])
-- In the expression: 1
-- In the expression: if True then 1 else "foo"
-- In an equation for `it': it = if True then 1 else "foo"

->
可精简到一行 [myDropX.hs]

Copy
-- file: ch02/myDropX.hs
myDropX n xs = if n <= 0 || null xs then xs else myDropX (n - 1) (tail xs)

惰性求值

e.g. [isOdd.hs]

Copy
-- file: ch02/isOdd.hs
isOdd n = mod n 2 == 1

-- ghci> isOdd (1+2)
-- True

--in general:XXXXXX
-- first: (1+2)
-- second: mod 3 2
-- third: 1 == 1
-- True

--but in haskell:
--(1+2) 最后算！当真正有需要的时候再计算出 isOdd (1 + 2) 的值
--追踪未求值表达式的记录被称为块（chunk）

递归

注意惰性求值，递归 -> 终止递归 -> 递归返回
解释
e.g. [myDrop.hs]

多态

参数多态

Copy
Prelude> :type last
last :: [a] -> a
ghci> last [1,2,3]
3

如果将一个类型为 [Char] 的列表传给 last ，那么编译器就会用 Char 代换 last 函数类型签名中的所有 a ，从而得出一个类型为 [Char] -> Char 的 last 函数。诸如此类可以是 Int，可以是任何类型。这种类型的多态被称为参数多态。

参数多态没有办法知道输入参数的实际类型

缺少

Haskell 没有子类多态和强制多态（coercion polymorphism）。

多参数函数的类型

e.g.

Copy
Prelude> :type take
take :: Int -> [a] -> [a]