haskell趣学指南学习笔记3:函数的语法

函数的语法

本章讲解的是haskell中的语法,借助他们可以使得你的函数更加可读,更富有表现力.

3.1 模式匹配

模式匹配(pattern matching)通过检查特定的结构来检查是否匹配,并按照模式从中解析出数据

类似于c++等语言中函数和运算符的重载

万能模式(catchall pattern)

总能够匹配输入的参数,将其绑定到模式中的名字供我们使用

递归函数

如果一个函数的定义中调用了自身,那么,他将被称为递归函数

防止崩溃

尽量在定义模式时,设置一个万能模式,以防止程序崩溃

3.1.1 元组的模式匹配

既然没有内建的对三元组取项的操作,我们也可以自己写

这里就和列表推导式的_一样,只用一个泛变量(generic variable)占位即可

3.1.2 列表与列表推导式的模式匹配

列表推导式模式匹配

在列表推导式中也可以使用模式匹配,而且一旦模式匹配失败,就简单的挪到下一个元素,而不会被包含在列表内

列表模式匹配

对于普通列表也可以使用模式匹配
– 使用[]来匹配空列表
– 使用:[]来匹配非空列表(列表本质是::...[]的语法糖)
x:xs模式在haskell中运用非常广泛,尤其对于递归函数,但只能匹配长度大于等于一的列表

ps:error函数,会生成一个运行时错误,用参数中的字符串表示错误的描述,它会直接导致程序崩溃

  • x:[],x:y:[]形式等同于,[x,y]形式的匹配
  • (x:y:_)这样的模式就不能用方括号表示了
  • 不能在模式中使用++,加入试图在模式匹配中使用,编译器将无法分辨前者和后者

3.1.3 As模式

as模式(as-pattern)允许我们按模式把一个值分割成多个项,同时还保留对整体的引用

3.2 注意, 哨卫

哨卫(guard)

哨卫用于检查参数的性质是否为真,类似if,但在处理多个条件分支时,哨卫的可读性更高,且与模式匹配更加契合

  • 在第一个哨卫之前,函数名和参数之间并没有=
  • 通过单引号,不仅可以中缀的形式调用前缀函数,甚至可以以中缀的形式定义函数

3.3 where

非参数的局部关键字,可以使用where关键字定义,它们跟在稍微后面,对于哨卫的每个分支都可以使用

局部变量

  • 局部变量定义都必须对齐于同一列,否则haskell难以区分属于哪个代码块
  • where中定义的名字只对本函数及本模式可用
  • 想要在多个函数和模式中使用,需要全局变量

where里的模式匹配

where块中的函数

3.4 let

let表达式和where绑定很是类似
– where允许我们在函数底部绑定变量,对包括哨卫在内的整个函数可见
– let是一个表达式,允许我们在任何位置定义局部变量,且不能对其他哨卫可见

  • 于是,区别就在于,let是个表达式,表达式一定有返回值,便可以放在任意位置
  • 因为let是一个表达式,不能在不同的哨卫中被使用
  • 另一些人喜欢where是因为允许将主函数体距离函数声明与类型变量更远些,可读性更高

3.4.1 几个常见用法

在局部域中定义函数

在一行中绑定多个名字

当从一个元组中取值时

3.4.2 列表推导式中的let

  • 在这里,let表达式的用法和谓词差不多,只不过它是用来绑定变量
  • 列表推导式的(w,h)<-xs部分被称为生成器

3.4.3 GHCi中的let

  • 直接在ghci部分定义函数和常量时,let的in部分可以省略
  • 如果省略,名字的定义,将会在整个会话过程中可见

3.5 表达式

  • case表达式类似c\c++中的switch,它取一个变量,对应不同的值并选择代码块并执行
  • 实际上,函数定义中的模式匹配不过是case表达式的语法糖而已

非常简单如果一个模式没有匹配,就交给下一个模式,若到最后没有匹配,就产生错误

优势

函数参数的匹配模式只能在定义函数时使用,而case表达式的匹配模式可以用在任意一个地方

Tagged with:

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据