为什么80%的码农都做不了架构师?>>>   

树是在程序中常用的一个数据结构。例如编译器和解析器常常吧程序表示为树;XML文档结构也是树状的;还有一些集合是基于树的,例如红黑树。
接下来我们将通过一个计算器程序来研究树在Scala中是如何表示和操纵的。这个程序的目标是处理一些由整数常量、变量和加号组成的简单的算数表达式,例如1 + 2 和 (x + x ) + (7 + y )。
我们首先要决定如何表示这些表达式。最自然的方法就是树了,树的节点表示操作符(在这里只有加法),而树的叶节点表示值(这里表示常数和变量)。 在Java中,这样的树可以表示为一个超类的树的集合,节点由不同子类的实例表示。而在函数式语言中,我们可以使用代数类型(algebraic data-type)来达到同样的目的。Scala提供了一种介于两者之间的叫做条件类(Case Classes)的东西。
abstract class Tree case class Sum(l: Tree, r: Tree) extends Tree case class Var(n: String) extends Tree case class Const(v: Int) extends Tree
我们实际上定义了三个条件类 Sum ,Var 和 Const 。这些类和普通类有若干不同:
1. 实例化时可以省略new关键字(例如你可以使用 Const(5)而不必使用 new Const(5) )
2. 参数的getter函数自动定义(例如你可以通过c.v来访问类Const的实例c在实例化时获取的参数v)
3. 拥有默认的预定义equals和hashCode实现,这些实现可以按照值区别类实例是否相等,而不是通过用。
4. 拥有默认的toString实现。这些实现返回值的代码实现(例如表达式x+1可以被表达成Sum(Var(x),Const(1)))
5. 条件类的实例可以通过模式匹配进行分析,我们接下来就要讲这个特性。
现在我们已经定义了表示我们算数表达式的数据类型,于是我们可以开始给他们定义对应的操作。我们将会首先编写一个在上下文中下计算表达式的函数。这里的上下文指的是变量与值的绑定关系。例如表达式x+1在x=5上下文中应该得出结果6。
这样一来我们需要找到一个表示这种绑定关系的方法。当然我们可以使用某种类似hash-table的数据结构,不过我们也可以直接使用函数!一个上下文无非就是一个吧名称映射到值的函数。例如上面给出的{x → 5}的这个映射我们就可以在Scala中表示为:
{ case "x" => 5 }
这个定义了一个函数:当参数等于字符串"x" 时返回整数5,否则抛出异常。
在编写求值函数之前我们,我们需要给我们的上下文起个名字,以便在后面的代码里面引用。理所应当的我们使用了类型String=>Int,但 是如果我们给这个类型起个名字,将会让程序更加简单易读,而且更加容易维护。在scala中,这件事情可以通过以下代码完成:
type Environment = String => Int
从现在开始,类型Environment就当作String到Int的函数类型名来使用了。
现在我们可以开始定义求值函数了。从概念上来说,这是很简单的一个过程:两个表达式之和等于两个表达式分别求值后再求和;变量的值可以从上下文中提取;常量的值就是他本身。在Scala中表达这个没有什么难度:
def eval(t: Tree, env: Environment): Int = t match { case Sum(l, r) => eval(l, env) + eval(r, env) case Var(n) => env(n) case Const(v) => v }
求值函数通过对树t进行模式匹配来完成工作。直观的来看,上述代码的思路是十分清晰的:
1. 第一个模式检查传入的树的根节点是否是一个Sum,如果是,它将会吧树的左边子树赋值给l,右边的子树赋值给r,然后按照箭头后面的代码进行处理;这里的代码可以(并且的确)使用了在左边匹配时所绑定的变量,比如这里的l和r。
2. 如果第一个检查没有成功,表明传入的树不是Sum,程序继续检查他是不是一个Var;如果是,则吧变量名赋给n然后继续右边的操作。
3. 如果第二个检查也失败了,表示t既不是Sum也不是Var,程序检查他是不是Const。如果是着赋值变量并且继续。
4. 最后,如果所有检查都失败了。就抛出一个异常表示模式匹配失败。这只有在Tree的其他之类被定义时才可能发生。
我们可以看出模式匹配的基本思想就是试图对一个值进行多种模式的匹配,并且在匹配的同时将匹配值拆分成若干子项,最后对匹配值与其子项执行某些代码。
一个熟练的面向对象的程序员可能想知道为什么我们不吧eval定义为Tree或者其之类的成员函数。我们事实上可以这么做。因为Scala允许条件类象普通类那样定义成员。决定是否使用模式匹配或者成员函数取决于程序员的喜好,不过这个取舍还和可扩展性有重要联系:
1. 当你使用成员函数时,你可以通过继承Tree从而很容易的添加新的节点类型,但是另外一方面,添加新的操作也是很繁杂的工作,因为你不得不修改Tree的所有子类。
2. 当你使用模式匹配是,形势正好逆转过来,添加新的节点类型要求你修改所有的对树使用模式匹配的函数,但是另一方面,添加一个新的操作只需要再添加一个模式匹配函数就可以了。
下面我们来更详细的了解模式匹配,让我们再给表达式定义一个操作:对符号求导数。读者们也许想先记住下面关于此操作的若干规则:
1. 和的导数等于导数的和,
2. 如果符号等以求导的符号,则导数为1,否则为0.
3. 参数的导数永远为0。
上述规则可以直接翻译成Scala代码:
def derive(t: Tree, v: String): Tree = t match { case Sum(l, r) => Sum(derive(l, v), derive(r, v)) case Var(n) if (v == n) => Const(1) case _ => Const(0) }
这个函数使用了两个关于模式匹配的功能,首先case语句可以拥有一个guard子句:一个if条件表达式。除非guard的条件成立,否则该模式不会成功匹配。其次是通配符:_ 。这个模式表示和所有值匹配而不对任何变量赋值。
事实上我们还远没有触及模式匹配的全部精髓。但是我们限于篇幅原因不得不再此停笔了。下面我们看看这个两个函数是如何在一个实例上运行的。为了达到这个目前我们写了一个简单的main函数来对表达式(x + x ) + (7 + y )进行若干操作:首先计算当{x → 5, y → 7}时表达式的值,然后分别对x和y求导。
def main(args: Array[String]) { val exp: Tree = Sum(Sum(Var("x"),Var("x")),Sum(Const(7),Var("y"))) val env: Environment = { case "x" => 5 case "y" => 7 } println("Expression: " + exp) println("Evaluation with x=5, y=7: " + eval(exp, env)) println("Derivative relative to x:\n " + derive(exp, "x")) println("Derivative relative to y:\n " + derive(exp, "y")) }
执行程序,我们能得到以下输出:
Expression: Sum(Sum(Var(x),Var(x)),Sum(Const(7),Var(y))) Evaluation with x=5, y=7: 24 Derivative relative to x:
Sum(Sum(Const(1),Const(1)),Sum(Const(0),Const(0))) Derivative relative to y: Sum(Sum(Const(0),Const(0)),Sum(Const(0),Const(1)))
通过研究程序输出,我们能看到求导的输出可以在被打印之前简化,使用模式匹配定义一个简化函数是挺有意思的(不过也需要一定的技巧)工作。读者可以尝试自己完成这个函数。

更多精彩内容请 关注:http://bbs.superwu.cn

关注超人学院微信二维码:

转载于:https://my.oschina.net/crxy/blog/417898

Scala的模式匹配和条件类相关推荐

  1. Scala模式匹配--样例类--密封样例类--偏函数

    Scala模式匹配--样例类--密封样例类--偏函数 模式匹配 等价于java的switch caseval c = '+'c match{case '+' => println(111)cas ...

  2. Scala学习—模式匹配

    scala的模式匹配除了可以对值进行匹配之外,还可以对类型进行匹配.对Array和LIst的元素进行匹配.对case class进行匹配.甚至对有值或没值(Option)进行匹配. scala是没有j ...

  3. Scala之模式匹配

    模式匹配是Scala中非常有特色,非常强大的一种功能.模式匹配,其实类似于Java中的swich case语法,即对一个值进行条件判断,然后针对不同的条件,进行不同的处理. 但是Scala的模式匹配的 ...

  4. 大数据开发语言Scala(三)——伴生类和伴生对象

    Scala语言是完全面向对象的语言,所以并没有静态的操作(即在Scala中没有静态的概念).但是为了能够和Java语言交互(因为Java中有静态概念),就产生了一种特殊的对象来模拟类对象,该对象为单例 ...

  5. SAP S4HANA如何取到采购订单ITEM里的'条件'选项卡里的条件类型值?

    SAP S4HANA如何取到采购订单ITEM里的'条件'选项卡里的条件类型值? 最近在准备一个采购订单行项目的增强的function spec.其中有一段逻辑是取到采购订单行项目条件里某个指定的条件类 ...

  6. python wait之后怎么起起来_python wait方法_Python条件类| 带有示例的wait()方法

    python wait方法 Python Condition.wait()方法 (Python Condition.wait() Method) wait() is an inbuilt method ...

  7. Scala 基础(4)—— 类和对象

    1. 类.字段和方法 Scala 用 class 关键字定义类,一旦定义好一个类,就可以使用 new 关键字创建对象. Scala 使用 new 调用无参构造器时,可以不使用 (),推荐不使用括号: ...

  8. Mybatis+mysql动态分页查询数据案例——条件类(HouseCondition)

    package cn.bdqn.mhouse.entity; /*** * * 项目名称:house * 类名称:HouseCondition * 类描述: 动态查询房屋信息的条件类 * 创建人:Mu ...

  9. python wait方法_Python条件类| 带有示例的wait()方法

    python wait方法 Python Condition.wait()方法 (Python Condition.wait() Method) wait() is an inbuilt method ...

  10. python 示例_Python条件类| release()方法与示例

    python 示例 Python Condition.release()方法 (Python Condition.release() Method) release() is an inbuilt m ...

最新文章

  1. react单选框获取值
  2. Smartforms 设置纸张打印格式
  3. C++访问权限与继承
  4. 详解javascript中的call, apply
  5. makefile——小试牛刀
  6. 【转】HTML5杂谈 概念与现行游戏 割绳子 宝石迷阵
  7. Linux下shellcode编写
  8. 电路——I/O口定时翻转电平驱动蜂鸣器注意事项
  9. Django下载、安装
  10. php 上传文件大小设置,调整PHP上传文件大小限制
  11. 概率论与数理统计(6):数理统计的基本概念
  12. 2012年8月 至 2014年2月1日读书列表
  13. methodsignature java_Java MethodSignature.getMethod方法代碼示例
  14. 有没有什么软件可以把视频转文字?看看这些转换软件
  15. 淘宝女装店铺如何提升转化?
  16. PE工具制作(大白菜+U盘启动模式)+系统重装
  17. xcode更新一直失败的解决办法
  18. 红楼梦人物出场统计python_Python程序设计习题3——红楼梦人物出场次数统计
  19. wxpython 右键菜单_使用wxPython打造印象笔记(14)笔记本管理
  20. 利用Zotero进行文献检索与管理

热门文章

  1. 县级外业调查及举证软件_省三调办举办全省第三次全国国土调查统一时点更新省级技术培训会...
  2. node mysql gbk_nodejs gb2312、GBK中文乱码解决方法
  3. [翻译] 5点建议,让iOS程序跑得更快
  4. 表达式语言输出map
  5. 2006年,谁是我们的博客之星?
  6. 蓝桥杯 后缀字符串 map
  7. Double得有效位
  8. php vm_facebook hiphop php vm 兑现概述(二)
  9. mysql 5.7 主主配置文件_MySQL 5.7主主备份配置
  10. ping 丢包 网络摄像头_Ping丢包故障案例