类型#
特性#
强(strong)类型
静态(static)
可以通过自动推导(automatically inferred)得出
基本类型#
Char
Bool
Int 常用 定长(fixed-width)整数 一般32/64位宽
Integer 不常用 不定长
Double 常用 浮点数
Float 不常用 计算慢
Prelude> :type 'a'
'a' :: Char
Prelude> 'a' -- 自动推导
'a'
::符号
Prelude> 'a' :: Char -- 显式签名
'a'
调用函数#
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 取最后一个元素
Prelude> head [1, 2, 3, 4]
1
Prelude> head []
*** Exception: Prelude.head: empty list
Prelude> tail "list"
"ist"
ghci> last [1,2,3]
3
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
元组的类型由它所包含元素的数量、位置和类型决定
Prelude> :type (True, "hello")
(True, "hello") :: (Bool, [Char])
Prelude> (4, ['a', 'm'], (16, True))
(4,"am",(16,True))
Prelude> :t ()
() :: ()
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.:
Prelude> :type readFile
readFile :: FilePath -> IO String
-- 不纯函数的类型签名都以 IO 开头
加载.hs 文件#
ghci 中通过:load
变量#
一旦变量绑定了(也即是,关联起)某个表达式,那么这个变量的值就不会改变。如.hs 文件中就不行,但 ghci 命令行里可以。
e.g. [Assign.hs]
条件求值#
不同分支之间的类型必须相同。
e.g.:
[myDrop.hs]
-- 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]
-- file: ch02/myDropX.hs
myDropX n xs = if n <= 0 || null xs then xs else myDropX (n - 1) (tail xs)
惰性求值#
e.g. [isOdd.hs]
-- 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]
多态#
参数多态#
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.
Prelude> :type take
take :: Int -> [a] -> [a]