Scala函数式对象-有理数

有理数类的表示


实现规范:支持有理数的加减乘除,并支持有理数的规范表示

1.定义Rational


首先,考虑用户如何使用这个类,我们已经决定使用“Immutable”方式来使用Rational对象,我们需要用户在定义Rational对象时提供分子和分母。

class Rational(n:Int, d:Int)

可以看到,和Java不同的是,Scala的类定义可以有参数,称为类参数,如上面的n、d。Scala使用类参数,并把类定义和主构造函数合并在一起,在定义类的同时也定义了类的主构造函数。因此Scala的类定义相对要简洁些

Scala编译器会编译Scala类定义包含的任何不属于类成员和类方法的其它代码,这些代码将作为类的主构造函数。比如,我们定义一条打印消息作为类定义的代码:

scala> class Rational (n:Int, d:Int) { | println("Created " + n + "/" +d) | }
defined class Rationalscala> new Rational(1,2)
Created 1/2
res0: Rational = Rational@22f34036

可以看到创建Ratiaonal对象时,自动执行类定义的代码(主构造函数)。

2.重新定义类的toString方法


上面的代码创建Rational(1,2),Scala 编译器打印出Rational@22f34036,这是因为使用了缺省的类的toString()定义(Object对象的),缺省实现是打印出对象的类名称+“@”+16进制数(对象的地址),显示结果不是很直观,因此我们可以重新定义类的toString()方法以显示更有意义的字符。

在Scala中,你也可以使用override来重载基类定义的方法,而且必须使用override关键字表示重新定义基类中的成员。比如:

scala> class Rational (n:Int, d:Int) { | override def toString = n + "/" +d | }
defined class Rationalscala> val x= new Rational(1,3)
x: Rational = 1/3scala> val y=new Rational(5,7)
y: Rational = 5/7

 

3.前提条件检查


前面说过有理数可以表示为 n/d(其中d、n为正数,而d不能为0)。对于前面的Rational定义,我们如果使用0,也是可以的。

怎么解决分母不能为0的问题呢?面向对象编程的一个优点是实现了数据的封装,你可以确保在其生命周期过程中是有效的。对于有理数的一个前提条件是分母不可以为0,Scala中定义为传入构造函数和方法的参数的限制范围,也就是调用这些函数或方法的调用者需要满足的条件。Scala中解决这个问题的一个方法是使用require方法(require方法为Predef对象的定义的一个方法,Scala环境自动载入这个类的定义,因此无需使用import引入这个对象),因此修改Rational定义如下:

scala> class Rational (n:Int, d:Int) { | require(d!=0) | override def toString = n + "/" +d | }defined class Rational scala> new Rational(5,0) java.lang.IllegalArgumentException: requirement failed at scala.Predef$.require(Predef.scala:211) ... 33 elided 

可以看到,如果再使用0作为分母,系统将抛IllegalArgumentException异常。

4.添加成员变量


前面我们定义了Rational的主构造函数,并检查了输入不允许分母为0。下面我们就可以开始实行两个Rational对象相加的操作。我们需要实现的函数化对象,因此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)} 

实际上编译器会给出编译错误。

这是为什么呢?尽管类参数在新定义的函数的访问范围之内,但仅限于定义类的方法本身(比如之前定义的toString方法,可以直接访问类参数),但对于that来说,无法使用that.d来访问d。因为that不在定义的类可以访问的范围之内。此时需要定类的成员变量

注:后面定义的case class类型编译器自动把类参数定义为类的属性,这是可以使用that.d等来访问类参数)。

修改Rational定义,使用成员变量定义如下:

class Rational (n:Int, d:Int) { require(d!=0) val number =n val denom =d override def toString = number + "/" +denom def add(that:Rational) = new Rational( number * that.denom + that.number* denom, denom * that.denom )}

 

要注意的我们这里定义成员变量都使用了val,因为我们实现的是“immutable”类型的类定义。number和denom以及add都可以不定义类型,Scala编译能够根据上下文推算出它们的类型。

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 res0: Rational = 7/6 scala> oneHalf.number res1: Int = 1 

5.自身引用


Scala 也使用this来引用当前对象本身,一般来说访问类成员时无需使用this,比如实现一个lessThan方法,下面两个实现是等效的。

第一种:

def lessThan(that:Rational) = this.number * that.denom < that.number * this.denom 

第二种:

def lessThan(that:Rational) = number * that.denom < that.number * denom 

但如果需要引用对象自身,this就无法省略,比如下面实现一个返回两个Rational中比较大的一个值的一个实现:

def max(that:Rational) = if(lessThan(that)) that else this 

其中的this就无法省略。

6.辅助构造函数


在定义类时,很多时候需要定义多个构造函数,在Scala中,除主构造函数之外的构造函数都称为辅助构造函数(或是从构造函数),比如对于Rational类来说,如果定义一个整数,就没有必要指明分母,此时只要整数本身就可以定义这个有理数。我们可以为Rational定义一个辅助构造函数,Scala定义辅助构造函数使用 this(…)的语法,所有辅助构造函数名称为this。

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

所有Scala的辅助构造函数的第一个语句都为调用其它构造函数,也就是this(…)。被调用的构造函数可以是主构造函数或是其它构造函数(最终会调用主构造函数)。这样使得每个构造函数最终都会调用主构造函数,从而使得主构造函数称为创建类单一入口点。在Scala中也只有主构造函数才能调用基类的构造函数,这种限制有它的优点,使得Scala构造函数更加简洁和提高一致性。

7.私有成员变量和方法


Scala 类定义私有成员的方法也是使用private修饰符,为了实现Rational的规范化显示,我们需要使用一个求分子和分母的最大公倍数的私有方法gcd。同时我们使用一个私有变量g来保存最大公倍数,修改Rational的定义:

scala> class Rational (n:Int, d:Int) { | require(d!=0) | private val g =gcd (n.abs,d.abs) | val number =n/g | val denom =d/g | override def toString = number + "/" +denom | def add(that:Rational) = | new Rational( | number * that.denom + that.number* denom, | denom * that.denom | ) | def this(n:Int) = this(n,1) | private def gcd(a:Int,b:Int):Int = | if(b==0) a else gcd(b, a % b) | } defined class Rational scala> new Rational ( 66,42) res0: Rational = 11/7 

注意gcd的定义,因为它是个回溯函数,必须定义返回值类型。Scala 会根据成员变量出现的顺序依次初始化它们,因此g必须出现在number和denom之前。

8.定义运算符


本篇还将接着上篇Rational类,我们使用add定义两个Rational对象的加法。两个Rational加法可以写成x.add(y)或者x add y。
即使使用 x add y还是没有 x + y来得简洁。
我们前面说过,在Scala中,运算符(操作符)和普通的方法没有什么区别,任何方法都可以写成操作符的语法。比如上面的 x add y。
而在Scala中对方法的名称也没有什么特别的限制,你可以使用符号作为类方法的名称,比如使用+、-和*等符号。因此我们可以重新定义Rational如下:

class Rational (n:Int, d:Int) { require(d!=0) private val g =gcd (n.abs,d.abs) val numer =n/g val denom =d/g override def toString = numer + "/" +denom def +(that:Rational) = new Rational( numer * that.denom + that.numer* denom, denom * that.denom ) def * (that:Rational) = new Rational( numer * that.numer, denom * that.denom) def this(n:Int) = this(n,1) private def gcd(a:Int,b:Int):Int = if(b==0) a else gcd(b, a % b)} 

这样就可以使用 +、*号来实现Rational的加法和乘法。+、*的优先级是Scala预设的,和整数的+、-、*和/的优先级一样。下面为使用Rational的例子:

scala> val x= new Rational(1,2)
x: Rational = 1/2 scala> val y=new Rational(2,3) y: Rational = 2/3 scala> x+y res0: Rational = 7/6 scala> x+ x*y res1: Rational = 5/6 

从这个例子也可以看出Scala语言的扩展性,你使用Rational对象就像Scala内置的数据类型一样。

9.Scala中的标识符


从前面的例子我们可以看到Scala可以使用两种形式的标志符,字符数字和符号。字符数字使用字母或是下划线开头,后面可以接字母或是数字,符号“$”在Scala中也看作为字母。然而以“$”开头的标识符为保留的Scala编译器产生的标志符使用,应用程序应该避免使用“$”开始的标识符,以免造成冲突。

Scala的命名规则采用和Java类似的camel命名规则(驼峰命名法),首字符小写,比如toString。类名的首字符还是使用大写。此外也应该避免使用以下划线结尾的标志符以避免冲突。

符号标志符包含一个或多个符号,如+、:和?。对于+、++、:::、<、 ?>、 :->之类的符号,Scala内部实现时会使用转义的标志符。例如对:->使用$colon$minus$greater来表示这个符号。因此,如果你需要在Java代码中访问:->方法,你需要使用Scala的内部名称$colon$minus$greater。

混合标志符由字符数字标志符后面跟着一个或多个符号组成,如 unary_+为Scala对+方法的内部实现时的名称。

字面量标志符为使用‘’定义的字符串,比如 ‘x’ 、‘yield’ 。 你可以在‘’之间使用任何有效的Scala标志符,Scala将它们解释为一个Scala标志符,一个典型的使用是 Thread的yield方法, 在Scala中你不能使用Thread.yield()是因为yield为Scala中的关键字, 你必须使用 Thread.‘yield’()来使用这个方法。

10.方法重载


和Java一样,Scala也支持方法重载,重载的方法参数类型不同而使用同样的方法名称,比如对于Rational对象,+的对象可以为另外一个Rational对象,也可以为一个Int对象,此时你可以重载+方法以支持和Int相加

def + (i:Int) = new Rational (numer + i * denom, denom) 

11.隐式类型转换


上面我们定义Rational的加法,并重载+以支持整数,r + 2,当如果我们需要 2 + r如何呢?

可以看到 x + 3没有问题,3 + x就报错了,这是因为整数类型不支持和Rational相加。我们不可能去修改Int的定义(除非你重写Scala的Int定义)以支持Int和Rational相加。如果你写过.Net代码,这可以通过静态扩展方法来实现,Scala提供了类似的机制来解决这种问题。

如果Int类型能够根据需要自动转换为Rational类型,那么 3 + x就可以相加。Scala通过implicit def定义一个隐含类型转换,比如定义由整数到Rational类型的转换如下:

implicit def intToRational(x:Int) = new Rational(x) 

其实此时Rational的一个+重载方法是多余的, 当Scala计算 2 + r,发现 2(Int)类型没有可以和Rational对象相加的方法,Scala环境就检查Int的隐含类型转换方法是否有合适的类型转换方法,类型转换后的类型支持+ r,一检查发现定义了由Int到Rational的隐含转换方法,就自动调用该方法,把整数转换为Rational数据类型,然后调用Rational对象的 +方法。从而实现了Rational类或是Int类的扩展。关于implicit def的详细介绍将由后面的文章来说明,隐含类型转换在设计Scala库时非常有用。




转自:https://www.jianshu.com/p/61268f438485

转载于:https://www.cnblogs.com/duanxz/p/9509748.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/397144.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2020双十一实时大屏_2020拼多多双十一,拼多多双十一活动

2020拼多多双十一&#xff0c;拼多多双十一活动&#xff0c;2020拼多多双十一&#xff0c;拼多多双十一活动2020拼多多双十一&#xff0c;拼多多双十一活动拼多多双11来了全球狂欢节先领券再购物低价风暴 震撼来袭没有最低 只有更低拼多多优惠券商城拼多多优惠商城&#xff0c;…

dataTables本地刷新数据解决只能初始化一次问题

2019独角兽企业重金招聘Python工程师标准>>> dataTables的表格只能初始化一次&#xff0c;这样如果需要动态改变表格数据的话就需要写多个表格&#xff0c;这样很显然不是一个好的解决方案。 dataTables Api提供了刷新数据解决方案&#xff1a; 这里大概说一下案例&…

安装Ubuntu版本linux过程中没有提示设置root用户密码问题的解决办法

原来ubunto不提倡设置root用户&#xff0c;系统安装成功后&#xff0c;root密码是随机的&#xff0c;那么在这种情况下如何得到root权限呐&#xff0c;具体方法如下&#xff1a; 终端中输入&#xff1a;sudo passwd root 此时重新设置原登录用户的密码。 设置成功后在终端继续输…

linux命令headtail

一、head语法head [-n -k ]... [FILE]...//k是数字默认是显示开头前10行。head /etc/passwd显示开头前5行head -5 /etc/passwdhead -n 5 /etc/passwd&#xff08;注意和以下的有-的差别&#xff09;head -n 5 /etc/passwd 除最后k行外&#xff0c;显示剩余所有内容。head -n -5…

用-force –opengl 指令_苹果新系统ios14新功能汇总 轻点背面等小技巧怎么用

在 iOS 14 以及更新系统中&#xff0c;苹果为 iPhone X 以及更新机型带来了“轻点背面”功能&#xff0c;可以让用户轻点手机背面来实现更多操作&#xff0c;并且这项功能还支持“快捷指令”。例如&#xff0c;如果您不希望应用读取剪贴板中私密内容&#xff0c;可以利用“轻点…

PE文件格式(加密与解密3)(一)

本次的了解主要讲解 PE的基本概念、MS-DOS文件头、PE文件头、区块、输入表、输出表等。 这里我将会结合一个简单的小程序来加深我对PE文件结构的了解。 使用学习工具&#xff1a;有StudyPE、LordPE、PEID。 学习PE建议看书。。和自己动手。。。 PE文件&#xff1a; 在WIN上&…

mysql用户_MySQL用户权限管理详解

用户权限管理主要有以下作用&#xff1a;1. 可以限制用户访问哪些库、哪些表2. 可以限制用户对哪些表执行SELECT、CREATE、DELETE、DELETE、ALTER等操作3. 可以限制用户登录的IP或域名4. 可以限制用户自己的权限是否可以授权给别的用户一、用户授权mysql> grant all privile…

对ContentProvider中getType方法的一点理解

在上篇博客中我们介绍了自定义ContentProvider&#xff0c;但是遗漏掉了一个方法&#xff0c;那就是getType&#xff0c;自定义ContentProvider一般用不上getType方法&#xff0c;但我们还是一起来探究下这个方法究竟是干什么的&#xff1f;我们先来看看ContentProvider中对这个…

手把手教Electron+vue的使用

.现如今前端框架数不胜数&#xff0c;尤其是angular、vue吸引一大批前端开发者&#xff0c;在这个高新技术快速崛起的时代&#xff0c;自然少不了各种框架的结合使用。接下来是介绍electronvue的结合使用。 2.Electron是什么&#xff1f;&#xff1f; 对于我来说Electron相当于…

shell循环和分支

循环和分支对代码块的操作是构造组织shell脚本的关键. 循环和分支结构为脚本编程提供了操作代码块的工具.10.1. Loops循环就是重复一些命令的代码块,如果条件不满足就退出循环.for loopsfor arg in [list]这是一个基本的循环结构.它与C的for结构有很大不同.forarg in [list]do …

mysql主从_MySQL主从原理及配置详解

MySQL主从配置及原理&#xff0c;供大家参考&#xff0c;具体内容如下一、环境选择&#xff1a;1.Centos 6.52.MySQL 5.7二、什么是MySQL主从复制MySQL主从复制是其最重要的功能之一。主从复制是指一台服务器充当主数据库服务器&#xff0c;另一台或多台服务器充当从数据库服务…

引导修复 不是活动的_河南省视频数据修复中心

河南省视频数据修复中心 lk6afds河南省视频数据修复中心 文件预览我找到了我要恢复文件&#xff0c;可是&#xff0c;这个文件能能正确恢复呢。没有用的文件不可以删掉吗。我们先来看看盘文件夹都是什么吧。(以下仅限于~系统)一般来说&#xff0c;刚刚安装的电脑系统盘主要包含…

企业日志分析 五大问题需重点注意

资讯 | 安全 | 论坛 | 下载 | 读书 | 程序开发 | 数据库 | 系统 | 网络 | 电子书 | 微信学院 | 站长学院 | 源码 | QQ | 专栏 | 考试 | 系统安全| 网站安全| 企业安全| 网络安全| 工具软件| 杀毒防毒| 加密解密|首页 > 安全 > 企业安全 > 正文企业安全…

sqlite换成mysql_从SQLITE的数据转到MYSQL

接同事需求&#xff0c;要求从SQLITE的数据转到MYSQL&#xff0c;这东西以前也没接触过。这里搜搜&#xff0c;那里试试&#xff0c;下面把过程列一下。主要过程分三步&#xff1a;1&#xff0c;把SQLITE表结构导出来&#xff0c;作一定的格式调整2&#xff0c;把SQLITE数据导出…

python学习笔记(一):python入门

上周六终于开始接触心心念念的python了&#xff0c;本人学习语言算是零基础&#xff0c;java语法比较复杂&#xff0c;所以选择了一个语法相对还是比较简单&#xff0c;而且现在使用也是越来越广泛的python进行了学习。下面就言归正传吧 在学习python之前先来了解下现今比较流行…

MySQL查询优化之explain的深入解析

在分析查询性能时&#xff0c;考虑EXPLAIN关键字同样很管用。EXPLAIN关键字一般放在SELECT查询语句的前面&#xff0c;用于描述MySQL如何执行查询操作、以及MySQL成功返回结果集需要执行的行数。explain 可以帮助我们分析 select 语句,让我们知道查询效率低下的原因,从而改进我…

怎么验证proftpd安装成功_英雄联盟手游泰服安卓账号怎么注册

英雄联盟手游中泰服安卓账号怎么注册&#xff1f;泰服安卓账号的注册流程是怎样的&#xff1f;泰服安卓账号的注册与其他服安卓账号的注册是否一致&#xff1f;接下来就给介绍下手游中泰服安卓账号的注册&#xff0c;希望对各位玩家能有所帮助。英雄联盟游戏新泰服安卓账号怎样…

oracle实现mysql的if_oracle中decode函数 VS mysql中的if函数和case函数

oracle中有decode函数&#xff0c;如下&#xff1a;select sum(decode(sex&#xff0c;男&#xff0c;0,1)) 男生数 from school&#xff1b;统计男生数目&#xff0c;含义为&#xff1a;decode()中sex字段为男时&#xff0c;用1代替&#xff0c;然后计算总和而mysql中没有该函…

mysql 删掉重复数据

--不知道为啥这个mysql外边还要包一层&#xff0c;不然就报错DELETE FROMcourse WHEREname IN ( select mm.name from (SELECTa.name as nameFROMcourse aGROUP BYa. NAMEHAVINGcount(a.NAME) > 1)mm) AND id NOT IN ( select nn.id from (SELECTmin(id) as idFROMcours…

spring中用到哪些设计模式

1.工厂模式&#xff0c;这个很明显&#xff0c;在各种BeanFactory以及ApplicationContext创建中都用到了&#xff1b; 2.模版模式&#xff0c;这个也很明显&#xff0c;在各种BeanFactory以及ApplicationContext实现中也都用到了&#xff1b; 3.代理模式&#xff0c;在Aop实现中…