𝖄𝕺🌎𝕿𝕽𝕺¥

𝖄𝕺🌎𝕿𝕽𝕺¥

𝕴 𝖉𝖔 𝖒𝖆𝖌𝖎𝖈
github

Haskell笔记二

类型#

特性#

强(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]
加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。