Haskell类型系统
静态、强类型检查使得 Haskell 更安全,而类型推导则让它更精炼、简洁。正是自动推导帮助 Haskell 程序既可以获得静态类型带来的所有好处,而又不必像传统的静态类型语言那样,忙于添加各种各样的类型签名(如 C 语言的函数原型声明)
强类型系统使Haskell拒绝执行任何无意义的表达式,并且不会自动地将值从一个类型转换到另一个类型;静态类型系统则使Haskell编译器可以在编译期(而不是执行期)知道每个值和表达式的类型;Haskell类型可以通过自动推导得出。
类型签名
类型签名(v.) 即为表达式显式的指明类型
输入var::T
,向解释器或编译器表明var的类型是T。显示签名必须正确,否则 Haskell 编译器就会产生错误;若未显式签名,则类型由自动推导决定。
一些基本类型
Char
单个Unicode字符
Bool
表示一个布尔逻辑值,该类型只有两个值: True 和 False
Int
带符号的定长整数
Integer
带符号的非定长整数
Double
浮点数
复合数据类型
列表 [a]
性质:类型多态(polymorphic)列表元素的值可以是任意类型(但所有元素必须同种类型)
方法:
- head 返回列表的第一个元素
- tail 返回列表里除了第一个元素之外的其他元素
- init 返回除了最后一个元素的其他部分
- last 返回最后一个元素
- take n list 从list中取前n个元素组成list,n>=0,如果n=0,得到一个空list
- drop n list 删除一个 List 中的前n个元素
- length 返回长度
- null 检测是否为空
- reverse 反转序列
- maximum/minimum 得到最大/小元素(list中的元素必然可以比较)
- sum 求和
- elem x list 判断x是否存在于list中
- notElem
- concat 取一个包含列表的列表 去掉一级的嵌套 连接列表在成为一个单一的列表
- and、or 针对包含Bool值的列表 相当于用&&和||遍历这个列表并两两求值
- splitAt 返回由一个列表产生的二元组 两部分是由原来的列表根据给定的索引分割而成
- takeWhile、dropWhile 从开头遍历一个列表 抽取或抛弃使谓词返回True的元素(实际上两个函数都是走到第一个使谓词返回False的元素处就停止操作,即使这个元素后面还有使谓词返回True的元素,两个函数也不再take或drop)
- break、span 提取列表中使谓词失败、成功(即第一次使谓词成功、失败的所有前驱)的元素组成二元组的首项
- filter 返回列表中使谓词成功的每一个元素
- Data.List.isPrefixOf xs xs_ 判断左边列表是否出现在右边的列表的开始处
- Data.List.isInfixOf xs xs_ 判断左边列表是否是右边的列表的一个子列表
- Data.List.isSuffixOf xs xs_
- zip xs xs_ 把两个列表压缩成一个单一的由二元组组成的列表,如果两个list中的元素个数不一致,以较短的那个为界
- zipWith func xs xs_ 它带两个列表作为参数并为从每个列表中抽取一个元素而组成的二元组提供一个函数,最后生成与较短的那个列表等长的新列表
元组
性质:元组的类型由它所包含元素的数量、位置和类型决定(如果两个元组里都包含着同样类型的元素,而这些元素的摆放位置不同,那么它们的类型就不相等)
方法:
- fst 返回首项
- snd 返回次项
调用函数
compare (sqrt 3) (sqrt 6)
- 函数应用的优先级比操作符要高
- 调用一个函数,先写出它的名字,后接函数的参数
- 函数的参数不需要用括号来包围,参数和参数之间不用逗号而用空格来隔开
- 为了可读性考虑,可以添加一些额外的括号,示例调用,若不加括号,Haskell认为我们将四个参数传给了只需要两个参数的 compare 函数
传表达式给函数
Haskell 的函数应用是左关联的。比如说,表达式 a b c d 等同于 (((a b) c) d),要将一个表达式用作另一个表达式的参数,那么就必须显式地使用括号来包围它,若移走括号,那么编译器就会认为我们试图将多个参数传给函数。
函数类型
lines::String->[String]
String -> [String]
的实际意思是指 lines 函数定义了一个从 String 到 [String] 的函数映射。
理解为,lines 函数接受一个字符串作为输入,并将这个字符串按行转义符号(换行符)分割成多个字符串后返回字符串列表。所以,函数的类型签名可以提示函数自身的功能。
多参数函数的类型
take::Int->[a]->[a]
类型签名中的 -> 符号是右关联的:Haskell 从右到左地串联起这些箭头,使用括号可以清晰地标示即take::Int->([a]->[a])
。
条件求值:if表达式
--file myDrop.hs
myDrop n xs = if n<=0 || null xs
then xs
else myDrop (n-1) (tail xs)
- 跟在 if 之后的是一个 Bool 类型的表达式,它是 if 的条件部分
- 跟在 then 关键字之后的是另一个表达式,这个表达式在条件部分的值为 True 时被执行
- 跟在 else 关键字之后的又是另一个表达式,这个表达式在条件部分的值为 False 时被执行
- 跟在 then 和 else 之后的表达式称为“分支”。不同分支之间的类型必须相同
多态
当需要编写带有多态类型的代码时,需要使用类型变量。这些类型变量以小写字母开头,作为一个占位符,最终被一个具体的类型替换。
如,[a] 用一个方括号包围一个类型变量 a ,表示一个“类型为 a 的列表”,当需要一个带有具体类型的列表时,就需要用一个具体的类型去替换类型变量, [Int] 表示一个包含 Int 类型值的列表,它用 Int 类型替换了类型变量 a 。
练习
写一个函数 lastButOne, 返回列表倒数第二个元素
--file lastButOne.hs
lastButOne xs=if length xs==2
then head xs
else lastButOne (tail xs)
该函数输入列表长度须大于等于2,否则引发异常Exception: Prelude.tail: empty list
,即异常原因是tail函数应用于空列表。
网友评论