学习Scala中的Rational类:分数的模型化

开发 后端
本文节选自Martin Odersky,Lex Spoon和Bill Venners所著,Regular翻译的《Programming in Scala》的第六章。Scala是一种针对 JVM 将函数和面向对象技术组合在一起的编程语言。

Rational类的式样书

分数:rational number是一种可以表达为比率n/d 的数字,这里的nd是数字,其中d不能为零。n被称作是分子:numeratord被称作是分母:denominator。分数的例子有:1/22/3112/2392/1 。与浮点数相比较,分数的优势是小数部分得到了完全表达,没有舍入或估算。

51CTO编辑推荐:Scala编程语言专题

本章我们将要设计的类必须模型化分数的行为,包括允许它们执行加,减,乘还有除运算。要加两个分数,首先要获得公分母,然后才能把两个分子相加。例如,要计算1/2+2/3 ,先把左操作数的上下部分都乘上3,右操作数的两部分都乘上2,得到了3/6+4/6 。把两个分子相加产生结果,7/6 。要乘两个分数,可以简单的两个分子相乘,然后两个分母相乘。因此,1/2*2/5 得到了2/10 ,还可以简化表示成它的“通常”形式1/5 。除法是把右操作数分子分母调换,然后做乘法。例如1/2/3/51/2*5/3相同,结果是5/6

一个或许不怎么重要的发现是,在数学上,分数不具有可变的状态。一个分数加到另外一个分数上,产生的结果是一个新的分数。而原来的数不会被“改变”。我们将在本章设计的不可变的Rational类将秉承这一属性。每个分数将都被表示成一个Rational对象。当两个Rational对象相加时,一个新的带着累加结果的Rational对象将被创建出来。

本章还将捎带提一些Scala让你写出感觉像原生语言支持的库的方法。例如,在本章结尾你将能用Rational类这样做:

  1. scala> val oneHalf = new Rational(12)  
  2. oneHalf: Rational = 1/2 
  3. scala> val twoThirds = new Rational(23)  
  4. twoThirds: Rational = 2/3 
  5. scala> (oneHalf / 7) + (1 twoThirds)  
  6. res0: Rational = 17/42 
创建Rational类

开始设计Rational类的着手点是考虑客户程序员将如何创建一个新的Rational对象。假设我们已决定让Rational对象是不可变的,我们将需要那个客户在创建实例时提供所有需要的数据(本例中,是分子和分母)。因此,我们应该这么开始设计:

  1. class Rational(n: Int, d: Int) 
这行代码里首先应当注意到的是如果类没有主体,就不需要指定一对空的大括号(当然你如果想的话也可以)。在类名,Rational,之后括号里的n和d,被称为类参数:class parameter。Scala编译器会收集这两个类参数并创造一个带同样的两个参数的主构造器:primary constructor

不可变对象的权衡

不可变对象提供了若干强于可变对象的优点和一个潜在的缺点。首先,不可变对象常常比可变对象更具逻辑性,因为它们没有随着时间而变化的复杂的状态空间。其次,你可以很自由地传递不可变对象,而或许需要在把可变对象传递给其它代码之前,需要先建造个以防万一的副本。第三,没有机会能让两个同时访问不可变对象的线程破坏它合理构造的状态,因为根本没有线程可以改变不可变对象的状态。第四,不可变对象让哈希表键值更安全。比方说,如果可变对象在被放进了HashSet之后被改变,那么你下一次查找这个HashSet就找不到这个对象了。

不可变对象唯一的缺点就是它们有时需要复制很大的对象图而可变对象的更新可以在原地发生。有些情况下这会变得难以快速完成而可能产生性能瓶颈。结果,要求库提供可变替代以使其更容易在大数据结构的中间改变一些元素也并非是一件稀奇的事情。例如,类StringBuilder是不可变的String的可变替代。

注意

这个最初的Rational例子凸显了Java和Scala之间的不同。Java类具有可以带参数的构造器,而Scala类可以直接带参数。Scala的写法更简洁——类参数可以直接在类的主体中使用;没必要定义字段然后写赋值函数把构造器的参数复制到字段里。这可以潜在地节省很多固定写法,尤其是对小类来说。

Scala编译器将把你放在类内部的任何不是字段的部分或者方法定义的代码,编译进主构造器。例如,你可以像这样打印输出一条除错消息:

  1. class Rational(n: Int, d: Int) {  
  2.  println("Created "+n+"/"+d)  
  3. }  
根据这个代码,Scala编译器将把println调用放在Rational的主构造器。因此,println调用将在每次创建一个新的Rational实例时打印这条除错信息:

  1. scala> new Rational(12)  
  2. Created 1/2 
  3. res0: Rational = Rational@a0b0f5 

【相关阅读】

  1. Scala中的富包装器:富操作和富类列表
  2. Scala操作符的优先级和关联性
  3. Scala对象的相等性比较
  4. Scala的数学运算、关系和逻辑操作及位操作符
  5. Scala的操作符:任何方法都可以是操作符
责任编辑:book05 来源: Artima
相关推荐

2009-07-08 15:35:18

Case类Scala

2009-07-21 12:18:37

ScalaRational对象toString

2009-07-22 07:57:00

ScalaCurry化函数

2009-07-22 07:53:00

Scala扩展类

2009-07-20 16:56:51

Scala类的定义

2009-09-28 11:37:03

Journal.scaKestrel

2010-06-18 09:29:37

UML与Rationa

2009-07-21 16:58:31

Scala变量范围

2012-11-23 14:28:45

IBMdW

2009-07-22 07:43:00

Scala闭包

2009-11-16 17:04:46

Inside Scal

2009-07-22 09:31:59

Scala类类层级Java类

2009-06-16 17:54:38

Scala类语法语义

2010-01-13 10:52:46

Rational Ro

2014-04-23 09:21:38

大数据

2009-07-22 07:50:00

Scala二维布局库抽象类

2009-11-09 11:04:51

ibmdwRational

2009-09-09 11:37:08

Scala的模式匹配

2009-07-21 08:54:35

Scala富包装器

2009-09-22 10:15:42

PersistentQScala
点赞
收藏

51CTO技术栈公众号