Scala 第十一章 操作符 and 习题答案

摘自《快学Scala》第十一章

0.重点

  • 标识符由字母,数字或运算符构成
  • 一元和二元操作符其实是方法调用
  • 操作符优先级取决于第一个字符,而结合性取决于最后一个字符
  • apply和update方法在对expr(args)表达式求值时被调用
  • 提取器从输入中提取元组或值的序列

1.标识符

一般还是用经典的模式:字母和数字的序列,以字母或下划线开头。

2.中置操作符

a 标识符 b

其中,标识符代表一个带有两个参数的方法(一个隐式的参数和一个显式的参数),如:

1 to 10
//实际上是
1.to(10)

这样的表达式称为中置表达式。如:

1 -> 10
//等同于
1.->(10)

在类中定义操作符:

//定义2个分数的乘积
// (n1 / d1) x (n2 / d2) = (n1n2 / d1d2)
class Fraction(n:Int,d:Int){
    private int num = ...
    private int den = ...
    ...
    def *(other:Fraction) = new Fraction(num * other.num, den * other.den)
}

3.一元操作符

只有一个参数的操作符称为一元操作符。
后置操作符(postfix)

a 标识符  等同于 a.标识符()
1 toString 等同于 1.toString() 

前置操作符(prefix):

+ - ! ~

它们被转换成对名为unary_操作符的方法调用,例如:

-a 等同于 a.unary_-

6.结合性

在Scala中,所有的操作符都是左结合的,除了:

  • 以冒号(:)结尾的操作符
  • 赋值操作符
  • 用于构造列表的::操作符是右结合的。如
1 :: 2 :: Nil
等同于
1 :: (2 :: Nil)

我们首先要创建出包含2的列表,这个列表又被作为尾巴并到以1作为头部的列表中。

右结合的二元操作符是其第二个参数的方法,如:

2 :: Nil
等同于
Nil.::(2)

7.apply和update方法

Scala允许你将如下的函数调用语法:

f(arg1, arg2, ...)

扩展到可以应用于函数之外的值,如果f不是函数或方法,那么这个表达式等同于调用:

f.apply(arg1, arg2,...)

如果它出现在赋值语句的等号左侧,表达式

f(arg1, arg2,...) = value
//等同于
f.update(arg1, arg2,...,value) 

这个机制被用于数组和映射。例如:

val scores = new scala.collection.mutable.HashMap[String,Int]
scores("Bob") = 100          //调用scores.update("Bob",100)
val bobsScore= scores("Bob") //调用scores.apply("Bob")

apply方法同样被经常用在伴生对象中,用来构造对象而不是显式的使用new。假定我们有一个Fraction类:

class Fraction(n:Int, d:Int){
    ...
}
object Fraction{
    def apply(n:Int, d:Int) = new Fraction(n,d)
}

val result = Fraction(3,4) * Fraction(2,5)

8.提取器

所谓提取器,就是一个带有unapply方法的对象,可以把unapply方法当做是伴生对象中apply方法的反向操作。apply方法接受构造参数,然后将它们变成对象,而unapply方法接受一个对象,然后从中提取值——通常这些值就是当初用来构造该对象的值。
unapply方法返回的是一个Option,它包含一个元组,每个匹配到的变量各有一个值与之对应。在本例中,我们返回一个Option[(Int,Int)]

Object Fraction{
    def unapply(input:Fraction) = 
        if (input.den == 0) None else Some((input.num, input.den))
}

你可以用提取器从任何类型的对象中提取信息。
举例来说,假定你想要从字符串中提取名字和姓氏:

val author = "Cay Horstmann"
val Name(first, last) = author  //调用Name.unapply(author)

提供一个对象Name,其unapply方法返回一个Option[(String,string)].如果匹配成功,返回名字和姓氏的对偶。该对偶的两个组成部分将会分别绑到模式中的两个变量。如果匹配失败,则返回None.

object Name{
    def unapply(input:String) = {
        val pos = input.indexOf(" ")
        if(pos == -1) None
        else Some((input.substring(0,pos), input.substring(pos+1)))
    }
}

每一个样例类都自动具备apply和unapply方法:

case class Currency(value:Double, unit:String)

//可以构造这样一个Currency实例
Currency(29.95, "EUR")

case Currency(amount, "USD") => println("$" + amount)

9.带单个参数或无参数的提取器

在Scala中,并没有只带一个组件的元组。如果unapply方法要提取单值,则它应该返回一个目标类型的Option,例如:

object Number{
    def unapply(input:String):Option[Int] = 
    try{
        Some(Integer.parseInt(input.trim))
    }catch{
        case ex:NumberFormException => None
    }
}
//用这个提取器,你可以从字符串中提取数字
val Number(n) = "1729"
//提取器也可以只是测试输入而并不真的将值提取出来,这样的话,unapply方法返回Boolean。如:
object IsCompound{
    def unapply(input:String) = input.contains(" ")
}

//可以用这个提取器给模式增加一个测试,如:
author match{
    case Name(first, last @ IsCompound()) => ...
    //如果作者是 Peter van der Linden 也能成功匹配
    case Name(first, last) => ...
}

10.unapplySeq方法

要提取任意长度得到值的序列,我们应该用unapplySeq来命名我们的方法它返回一个Option[Seq[A]],其中A是被提取的值的类型。如,Name提取器可以产出名字中所有组成部分的序列:

object Name{
    def unapplySeq(input:String):Option[Seq[String]] = 
        if(input.trim == "") None else Some(input.trim.split("\\s+"))
}

这样,就能匹配并取到任意数量的变量了:

author match{
    case Name(first, last) => ...
    case Name(first, middle, last) => ...
    case Name(first, "van","der",last) => ...
    ...
}

课后习题答案

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值