Function
defineName param = f(param)
模式匹配(pattern matching)为不同的模式分别定义函数体,注意匹配所有情况的模式应写到最后,类似switch/case,但匹配的是结构,关系具体内容的是哨位(guard)。
某种意义上函数名后的参数判断本身就是“模式匹配”,注意类型注解和模式匹配中的括号意味着元组和表达式
两种情况,由于元组内部被逗号分隔,容易分辨
defineName1 :: type1->type2
defineName1 a = f()
defineName1 x = f(x)
guard通过检查参数的性质是否为真(布尔判断),契合模式匹配的多条件分支。模式与guard相似的地方是对内容的判断(模式的结构包含内容),稍微不同的是guard能对参数组合后的内容进行判断。
元组的模式匹配指将每个成员分别写出来
列表中[]匹配空列表,可以用: 与 []匹配非空列表,x:xs将列表的头部绑定为x,余下部分为xs。
x:y:[]相当于[x,y]匹配长度固定的列表。
不能在列表的模式匹配中使用++,
as模式(as-pattern),允许按模式将一个值分隔成多个项,同时仍保留对其整体的引用。
如all@(x:y)
Prelude> firstletter all@(x:y) = "The first letter of "++all++" is "++[x]
Prelude> firstletter ['a','c','d']
"The first letter of acd is a"
case
模式匹配是case表达式的语法糖,函数参数的模式匹配只能在定义函数时使用,而case表达式可用在任何地方,始终没有匹配的模式,将会产生运行时错误,注意用空格缩进。注意typeclass与pattern的差别。
case expression of pattern -> result
pattern -> result
pattern -> result
...
where
函数where中定义的名字只对本函数可见,不必担心它会污染其它函数的命名空间。如果要对不同的函数重复使用到同一名字,应把它置于全局定义中。可以在where绑定中使用模式匹配和定义函数,但where中定义的名字不能在函数的其它模式访问?,最后一个模式可见,guard也可见。
let
let表达式的格式为let <bindings> in <expressions>。绑定的名字仅对in部分可见,本身是表达式一定有返回值。let把绑定语句放前面,后跟表达式,与where相反。(指where与其函数的整体)。直接在GHCi中定义let可省略in,这样名字的定义在整个会话中可见,由于没有in也不会出现返回值。
Prelude> let havefun x y = [x..y]
Prelude> havefun 1 5
[1,2,3,4,5]
Prelude> let hadfun x y = [x..y] in hadfun 3 7
[3,4,5,6,7]
Prelude> hadfun
<interactive>:61:1: error:
• Variable not in scope: hadfun
• Perhaps you meant ‘havefun’ (line 58)
list (列表是单类型的)
- 字符串是一组字符组成的列表,"hello"是['h','e','l','l','o']的语法糖
- [1,2,3]是1:2:3:[]的语法糖
- 嵌套列表的元素同样不能混合放置类型
a. 拼接字符串 list ++ list(会遍历第一个列表)
b. 添加字符串 element : list
c. 访问元素 list(string) !! index
d. 比较元素 <、 >、 >=、<=、==、 /=
e. 逻辑符号 &&、||、not
f. 列表头尾操作 head、tail、last、init (不要用到空列表[]上)
g. 列表属性 length返回长度,null检查是否为空,reverse反转列表,maximum取最大元素,minimum取最小元素,sum与product返回和与积
h. 截取操作,take number list 取出列表的前number个,返回列表;drop number list 返回删除前number个的列表。element elem list判断元素是否包含于列表
i. 区间,[a,n*a..b], [a..b],[a,n*a..]。cycle函数接受一个列表作为参数并返回一个无限列表;repeat函数接受一个值作为参数,返回一个仅包含该值的无限列表。replicate接受列表长度和复制的元素,takeWhile取一个谓词和列表作为参数,在元素符合谓词时返回元素,一旦不符合条件就停止,返回结果列表,注意与filter的区别。
j. odd 判断一个数是否为奇数,even判断一个数是否为偶数
Prelude> take 7 (repeat 6)
[6,6,6,6,6,6,6]
Prelude> take 17 (cycle "yukipedia")
"yukipediayukipedi"
Prelude> [0.1,0.2..1]
[0.1,0.2,0.30000000000000004,0.4000000000000001,0.5000000000000001,0.6000000000000001,0.7000000000000001,0.8,0.9,1.0]
Prelude> [1,(-2)..(-20)]
[1,-2,-5,-8,-11,-14,-17,-20]
list comprehension
[f(x,y)|x<-set,y<-set,predicate(x,y),let <bindins> in <expressions>]
列表推导式自然是产生列表的咯
分别演示了自定义length函数(每次元素满足谓词,执行f)、取出大写字符(处理字符串)、不拆开嵌套列表而去除奇数(始终返回列表)
Prelude> [x * y | x<-[1,2,3], y<-[10,12,14],x*y>20]
[24,28,30,36,42]
Prelude> length' xs = sum [1|_<-xs]
Prelude> length' [1..20]
20
Prelude> removeNonUppercase st = [c|c<-st,c `elem`['A'..'Z']]
Prelude> removeNonUppercase "yuKiPEdiA"
"KPEA"
Prelude> let nestList = [[2,3,4,5],[6,7,8,9],[10,11,12,13,14]]
Prelude> [[x|x<-nL,even x]|nL<-nestList]
[[2,4],[6,8],[10,12,14]]
tuple
元组是异构的,并且长度固定,由括号括起,由逗号隔开 。长度为2的元组(序对,pair)与长度为3的元组(三元组,triple)被视为不同的类型。进一步,两个元组要类型相同,需要每一项的类型都彼此对应。毫无疑问,每个元组都可能是个独特的类型。正因为如此,允许单元素的list,却不允许单元素的tuple。单纯用括号包含单元素,最终也只是作为表达式得到单元素。空元组类型为()。
- fst取一个序对作为参数,返回其首项。snd返回其尾项。
- zip list list 返回交叉配对的一组序对。
Prelude> zip [1..] ["yukino","yui","hiki"]
[(1,"yukino"),(2,"yui"),(3,"hiki")]
接下来演示一个实际问题,求三条边为1..10的整数,和为24的直角三角形。函数式一般思路:先去一个初始的集合并将其变形,随后持续地利用过滤条件缩小计算范围。
Prelude> let rightTriangles' = [(a,b,c)|c<-[1..10],a<-[1..c],b<-[a..c-1],a^2+b^2==c^2,a+b+c==24]
Prelude> rightTriangles'
[(6,8,10)]
基本数据类型
a.Int为有界整数,Integer无界,效率低于Int
b.Float 单精点浮点数,Double双精点
c.Bool布尔值
d.Char 表示一个Unicode字符,一个字符由单引号括起,一组字符的列表即字符串
e.凡是类型首字母大写,()除外
f.Odering类型有GT,LT,EQ三种值,分别表示大于,小于,等于
g.String是[Char]的别名
类型推断
:t 命令接受任何合法的表达式,返回其类型。:: 读作“它的类型为”。 后面可能会有type constraint,一定会有参数类型,最后一个参数代表返回值。
对函数而言可以“静态类型检查”诸如factorial1 :: Integer -> Integer factorial1 n = sum[1..n]
限制输入参数。
当对判断性质的运算符,如==,+,-,使用:t时,需要用(),如:t (==)。其实当中缀函数要作为参数或者前缀函数调用时,都需要用括号括起。可以中缀形式定义函数,通过反引号。
Prelude> :t "yukipedia"
"yukipedia" :: [Char]
Prelude> factorial n = product [1..n]
Prelude> :t factorial
factorial :: (Num a, Enum a) => a -> a
Prelude> :t head
head :: [a] -> a
类型注解(type annotation)
编译器可以辨认出大部分表达式的类型,但有时需要提示具体的类型。类型注解跟在表达式后面,通过 :: 分隔,显式表达类型。
Prelude> read "True"
*** Exception: Prelude.read: no parse
Prelude> read "True"||False
True
Prelude> [read "True",False]
[True,False]
Prelude> (read "True",False)
(*** Exception: Prelude.read: no parse
Prelude> read "True"::Bool
True
Prelude> read "True"::Int
*** Exception: Prelude.read: no parse
类型变量(type variable)
形如head函数的类型判断中的a,a可以是任何类型,使用类型变量的函数被称为多态函数(polymorphic function)。命名上,类型变量可以使用多个字符,不过一般约定为单字符,如a,b,c,d...。另外不同字符的类型变量并非意味两者表示的类型一定不同。
=>左侧叫做类型约束(type constraint),暂时认为类型变量始终泛指,而类型约束提供了范围
多态常量(polymorphic constant)指类型约束后 (typeclass a)=> a 这个函数输入输出没有变化?所有的数都是多态常量
Prelude> :t 7.0
7.0 :: Fractional p => p
Prelude> :t 7
7 :: Num p => p
类型类(typeclass)
typeclass是一组函数的集合,type constraint => type variable->type variable->..->type variable,所有变量都有类型,如果该类型是某typeclass的instance,那么它必须实现该类型所描述的行为。typeclass是定义行为的接口。即instance(变量所属的类型)具备执行“描述的行为”的能力,typeclass为定义行为的接口。
1.Eq类型类用于判可断相等性的类型,它的实例必须实现==和/=两个函数。
2.Ord类型类用于可比较大小的类型
3.Show类型类的实例为可以表示为字符串的类型
4.Read类型类与Show相反,将字符串转化为某类型
5.Enum类型类包含()、Bool、Char、Ordering、Int、Integer、Float、Double等类型(实例),可作为succ函数和pred函数的参数
6.Bounded类型类的实例类型可以通过maxBound和minBound得到其上限和下限,如果元组中的项的类型都属于Bounded类型类的实例,那么这个元组也属于Bounded的实例。
7.Num类型类的实例类型为Int、Integer、Float、Double等,只有属于Show和Eq的实例类型,才可以成为Num类型类的实例
8.Floating类型类仅包含Float和Double两种浮点类型
9.Integeral仅与整数有关,实例类型为Int和Integer
a.所有标准类型都是Eq类的实例(除与输入输出相关的类型和函数之外)
b.除函数以外,以上所有类型都是Ord,Read,Show的实例
c.typeclass含有多个类型作为实例,一个类型可以作为多个typeclass的实例。一个类型必须在成为某typeclass的实例后,才能成为另一个typeclas的实例
Prelude> :t fromIntegral
fromIntegral :: (Integral a, Num b) => a -> b
Prelude> fromIntegral (3)+3.2
6.2
Prelude> 3 + 3.2
6.2
Prelude> (3 :: Int)+3.2
<interactive>:39:12: error:
• No instance for (Fractional Int) arising from the literal ‘3.2’
• In the second argument of ‘(+)’, namely ‘3.2’
In the expression: (3 :: Int) + 3.2
In an equation for ‘it’: it = (3 :: Int) + 3.2
Prelude> (3 :: Float)+3.2
6.2
网友评论