scala函数式对象,欢迎广大码农批评指正

scala函数式对象
1、构建Rational类
Rational是有理数,本章将通过有理数的构建进行深入的讲解

学习完本章可以掌握编写类库的一些方法,感觉像原始语言支持的那样,通过本章的学习你将可以使用如下例子

val oneHalf=new Rational(1,2)

val twoThirds=new Rational(3,4)

(oneHalf/7)+(1-twoThirds)

构建Rational类要求你把实例所需要的数据(参数分子和分母)提供好,如下开始:

class Rational(n:Int,d:Int)

这段代码没有类的定义体,所以不需要写花括号,圆括号内的n和d作为参数,scala类会把参数放在主构造方法内。

我们要想构造Rational类需要对程序进行改进。

scala> class Rational(n:Int,d:Int){

 | println("Created:"+n+"/"+d)

 | }

defined class Rational

在shell端可以直接使用:new Rational(2,3)

scala> new Rational(2,3)

Created:2/3

res0: Rational = Rational@5117dd67

可以看到有了分数的雏形

2、重新实现toString
以上的输出Rational@5117dd67。解释器通过Rational对象调用toString来获取以上看上去非常奇怪的字符串。Rational默认继承了java.lang.Object的toString,这里只是简单的打印了类名@符号和一个十六进制的数字。toString主要意图是帮助程序员调试输出的语句、日志信息、测试失败报告、解释器和调试器输出中给出的相应信息。但本例的toString没有意义,因为没有提供任何和Rational(有理数)相关的任何线索。我们可以重写toString

class Rational(n:Int,d:Int){

 override def toString=n+"/"+d

}

override是重写的意思和java的概念相同。以上程序在shell进行直接调用

scala> new Rational(2,3)

res1: Rational = 2/3

3、前提条件检查
我们知道有理数或者说分数的分母不能为零,

scala> new Rational(2,0)

res2: Rational = 2/0

这种在现实是不允许的,所以在程序里我们也要处理。要将Rational的分母为0的情况设置为非法状态,当分母0作为参数传递进来时,不允许Rational被创建出来。

我们只需要对主构造函数进行值的前置约束即可。实现这个的方式是使用require。

class Rational(n:Int,d:Int){

 require(d!=0)

 override def toString=n+"/"+d

}

require会接受boolean的参数,传入为true返回正常结果,否则抛出java.lang.IllegalArgumentException异常

scala> new Rational(2,0)

java.lang.IllegalArgumentException: requirement failed

at scala.Predef$.require(Predef.scala:207)

… 34 elided

4、添加字段
主构造函数有了前置条件,接下来我们的注意力转向如何支持加法,我们可以在Rational类加一个add方法,方法内要接收另一个Rational作为参数,为了保持有理数本身不变所以不能把新的有理数加到自己身上。必须创建返回一个新的持有者两个有理数的和的Rational对象。

用以下方式

class Rational(n:Int,d:Int){

 require(d!=0)

 override def toString=n+"/"+d



 def add(that:Rational):Rational={

   new Rational(n*that.d+that.n*d,d*that.d)

 }

}

这段代码是有问题的,会出以下错误:

value d is not a member of Rational

value n is not a member of Rational

虽然n和d在add方法的作用域内,但只能访问执行调用add的那个对象上的n和d的值。当在add中用到n和d时,编译器提供这些参数对应的值,但是不允许使用that.n或that.d,因为that并非执行你执行的add调用的那个对象。要解决这个问题,需要把that的分子和分母做成字段。

我们添加两个字段,numer和demon,接受两个参数n和d。内部的每个用到参数n和d的地方都要相应的改成numer和demon。如下:

class Rational(n:Int,d:Int){

 require(d!=0)

 val numer:Int=n

 val demon:Int=d

 override def toString=numer+"/"+demon



 def add(that:Rational):Rational={

   new Rational(

       numer*that.demon+that.numer*demon,

       demon*that.demon

       )

 }

}可以直接进行实例化。

scala> val oneHalf=new Rational(1,2)

oneHalf: Rational = 1/2

scala> val twoThirds=new Rational(2,3)

twoThirds: Rational = 2/3

scala> oneHalf add twoThirds

res6: Rational = 7/6

再定义一个比较大小的方法:

 def lessThan(that:Rational)={

   this.numer*that.demon<that.numer*this.numer)

 }

这里的this是可以省略的效果一样。

但下面不能省略:

def max(that:Rational)={

 if(this.lessThan(that)) that else this

这里如果不写this就没有返回结果了。

5、添加第二个构造方法
当我们尝试给出一个参数的时候,我们的程序会怎么样呢?

scala> new Rational(3)

:12: error: not enough arguments for constructor Rational: (n: Int, d:

Int)Rational.

Unspecified value parameter d.

   new Rational(3)

结果告诉我们给的参数不够,针对这一情况我们要进行处理,我们处理的手段是添加一个辅助的构造方法。

class Rational(n:Int,d:Int){

 require(d!=0)

 val numer:Int=n

 val demon:Int=d

 def this(n:Int)=this(n,1)

 override def toString=numer+"/"+demon



 def add(that:Rational):Rational={

   new Rational(

       numer*that.demon+that.numer*demon,

       demon*that.demon

       )

 }



 def lessThan(that:Rational)=

   this.numer*that.demon<that.numer*this.numer



 def max(that:Rational)={

 if(this.lessThan(that)) that else this

}

}

我们在测试程序中进行测试,可以看到能够接受一个参数了。

测试结果

3/1

false

6、私有字段和方法
以上程序我们只是简单的使用了n和d分别初始化了numer和demon。实际上我们的分子和分母的概念可能更加复杂,比如44/66简化后就是2/3,但是我们的构造函数没有进行处理。所以要两个数除以最大公约数。

class Rational(n:Int,d:Int){

 require(d!=0)

 private val g=gcd(n.abs,d.abs)

 val numer:Int=n/g

 val demon:Int=d/g

 def this(n:Int)=this(n,1)

 override def toString=numer+"/"+demon

 def add(that:Rational):Rational={

   new Rational(

       numer*that.demon+that.numer*demon,

       demon*that.demon

       )

 }

   def lessThan(that:Rational)=

   this.numer*that.demon<that.numer*this.numer   //比较大小的函数



 def max(that:Rational)={

    if(this.lessThan(that)) that else this       //取最大值的函数

   }



 private def gcd(a:Int,b:Int):Int={              //求最大公约数的函数

   if(b==0)a else gcd(b,a % b)

 }

}

为了保证g永远是正数,所以使用了abs求绝对值,把一个字段或者函数变成私有的只需要加上关键字private即可。最大公约数的方法对于一般程序员来说不是难题,不过多介绍。

object Test{

def main(args: Array[String]): Unit = {

val t1=new Rational(2,32)

val t2=new Rational(3,4)

println(new Rational(100,50))

println(t1 lessThan(t2))

}

}

使出结果:

2/1

false

7、操作符的定义
对于程序的使用者来说,希望更加简单的方式来使用,只是使用+-/即可。我们需要把我们的程序进行进一步的调整,把我们定义的所有的函数都使用操作符来代替,在scala里允许把函数(java的方法)名定义成+ - /这样的符号。

class Rational(n:Int,d:Int){

 require(d!=0)

 private val g=gcd(n.abs,d.abs)

 val numer:Int=n/g

 val demon:Int=d/g

 def this(n:Int)=this(n,1)

 override def toString=numer+"/"+demon

 def +(that:Rational):Rational={

   new Rational(

       numer*that.demon+that.numer*demon,

       demon*that.demon

       )

 }

 def *(that:Rational):Rational=

   new Rational(numer*that.numer,demon*that.demon)



 def <(that:Rational)=

   this.numer*that.demon<that.numer*this.numer   //比较大小的函数



 def max(that:Rational)={

    if(this.<(that)) that else this       //取最大值的函数

   }



 private def gcd(a:Int,b:Int):Int={              //求最大公约数的函数

   if(b==0)a else gcd(b,a % b)

 }

}

针对已经定义好的 我们可以直接在测试程序中使用:

object Test{

def main(args: Array[String]): Unit = {

val t1=new Rational(2,32)

val t2=new Rational(3,4)

println(new Rational(100,50))

println(t1 <(t2))

println(t1 +t2)

println(t1 +t2*t1)

}

}

注意t1 +t2*t1这个操作会被当成t1 +(t2*t1)这样的方式来调用,因为*的优先级比+的优先级要高。在scala理还可以定义诸如++ – ::这样的操作符作为函数名。

8、方法重载
在我们的程序中,一个有理数乘一个正数效果不太理想,因为操作必须都是Rational,因此必须写成r new Rational(2),而不能写成b*2.通过添加两个新的方法来对有理数和正数做加法和乘法。

每个算数方法都提供两个版本:一个接收有理数作为参数,一个接收正数作为参数。也就是说每个算法都被重载了。

class Rational(n:Int,d:Int){

 require(d!=0)

 private val g=gcd(n.abs,d.abs)

 val numer:Int=n/g

 val demon:Int=d/g

 def this(n:Int)=this(n,1)

 override def toString=numer+"/"+demon

 def +(that:Rational):Rational={

   new Rational(

       numer*that.demon+that.numer*demon,

       demon*that.demon

       )

 }

   def +(i:Int):Rational={

   new Rational(numer+i*demon,demon)

 }

    def -(that:Rational):Rational={

   new Rational(

       numer*that.demon-that.numer*demon,

       demon*that.demon

       )

 }

   def -(i:Int):Rational={

   new Rational(numer-i*demon,demon)

 }



 def *(that:Rational):Rational=

   new Rational(numer*that.numer,demon*that.demon)

 def *(i:Int):Rational=

   new Rational(numer*i,demon)



 def /(that:Rational):Rational=

   new Rational(numer*that.demon,demon*that.numer)

 def /(i:Int):Rational=

   new Rational(numer,demon*i)



 def <(that:Rational)=

   this.numer*that.demon<that.numer*this.numer   //比较大小的函数



 def max(that:Rational)={

    if(this.<(that)) that else this       //取最大值的函数

   }



 private def gcd(a:Int,b:Int):Int={              //求最大公约数的函数

   if(b==0)a else gcd(b,a % b)

 }

}

程序编写好后可以在Test进行测试:

object Test{

def main(args: Array[String]): Unit = {

val t1=new Rational(2,32)

val t2=new Rational(3,4)

println(new Rational(100,50))

println(t1 <(t2))

println(t1 +t2)

println(t1*2)

println(t1/2)

println(t1 +t2*t1)

println(t1 *t2+t1)

}

}

运行结果如下:

2/1

false

13/16

1/8

1/32

7/64

7/64

想要免费获取本节资料或了解更多干货内容的小伙伴可以加QQ:773640911

猜你喜欢

转载自blog.csdn.net/qq_35128600/article/details/81477930