chapter 11 操作符
标签:快学scala
一、笔记
- scala种可以在反引号中包含几乎任何字符序列,
val ‘val‘ = 42
所有的操作符都是左结合的,除了以冒号(:)结尾的操作符,和赋值操作符。用于构造列表的::操作符是又结合的。1::2::Ni1的意思是1::(2::Ni1),先创建出包含2的列表,这个列表又被作为尾巴拼接到以1作为头部的列表中。
2. 函数调用语法:f(arg1, arg2,...)扩展到可以应用于函数之外的值,如果f不是函数或方法,那么这个表达式等于f.apply(arg1,arg2,....)。除非它出现在赋值语句的等号左侧。
f(arg1,arg2,...) = value 等同于如下调用
f.update(arg1,arg2,....,value)
- 提取器是一个unapply方法的对象,可以把unapply方法当做是伴生对象中apply方法的方向操作。apply接受构造参数,然后将它们变成对象,unapply方法接受一个对象,然后从中提取值,通常这些值就是当初用来构造该对象的值。
unapply方法返回的是一个Option,它包含一个元组,每个匹配到的变量各有一个值与之对应。
object Fraction{
def unapply(input: Fraction)=
if(input.den == 0) None else Some((input.num, input.den))
} //方法在分母为0时返回None,表示无匹配。
- 要提取任意长度的值的序列,我们应该用unapplySeq来命名我们的方法,它返回一个Option[Seq[A]],其中A是被提取的值的类型。比如,Name提取器可以产出名字中所有组成部分的序列:
object Name{
def unapplySeq(input: String): Option[Seq[A]] =
if(input.trim == "") None else Some(input.trim.split("\\s+"))
}
//匹配任意数量的变量
author match{
case Name(first, last) =>..
case Name(first, middle,last)=>..
...
}
二、习题答案
11.1 根据优先级规则,3 + 4 -> 5和3 -> 4 + 5是如何被求值的?
都是从左至右执行,所以 3 -> 4 + 5表达式是不合法的。
11.2 BigInt类有一个pow方法,但没有用操作符字符。Scala类库的设计者为什么没有选用**(像Fortran那样)或者^(像Pascal那样)作为乘方操作符呢?
scala中操作符就是方法,优先级是根据首字母来判断的。
最高优先级:除以下字符外的操作符字符
* / %
+ -
:
= !
< >
&
?
|
非操作符
最低优先级:赋值操作符
一般乘方操作符优先于乘法操作,如果使用**作为乘方的话,那么其优先级则与*相同,而如果使用^的话,则优先级低于*操作。优先级都是有问题的。故没有使用这两种操作符。
11.3 实现Fraction类,支持+*/操作。支持约分,例如将15/-6变为-5/2。除以最大公约数,像这样:
class Fraction(n:Int,d:Int){
private val num:Int = if(d==0) 1 else n * sign(d)/gcd(n,d);
private val den:Int = if(d==0) 0 else d * sign(d)/gcd(n,d);
override def toString = num + "/" + den
def sign(a:Int) = if(a > 0) 1 else if (a < 0) -1 else 0
def gcd(a:Int,b:Int):Int = if(b==0) abs(a) else gcd(b,a%b)
...
}
import scala.math.abs
class Fraction(n: Int, d: Int){
private val num: Int = if(d == 0)1 else n *sign(d)/gcd(n,d)
private val den: Int = if(d == 0)0 else d * sign(d)/gcd(n,d)
override def toString = num + "/" +den
def sign(a: Int) = if(a >0)1 else if(a <0) -1 else 0
def gcd(a: Int, b: Int): Int = if(b == 0) abs(a) else gcd(b, a%b)
def +(other: Fraction): Fraction= {
new Fraction((this.num * other.den) + (other.num *this.den), this.den * other.den)
}
def -(other: Fraction): Fraction={
new Fraction((this.num * other.den) - (other.num *this.den), this.den * other.den)
}
def *(other: Fraction): Fraction={
new Fraction(this.num * other.num, this.den * other.den)
}
def / (other: Fraction): Fraction={
new Fraction(this.num * other.den, this.den * other.num)
}
}
object Fraction{
def apply(n: Int, d: Int) = new Fraction(n, d)
}
val x = Fraction(15, -6)
val y = Fraction(20,60)
println(x)
println( x + y)
println( x * y)
11.4 实现一个Money类,加入美元和美分字段。提供+,-操作符以及比较操作符==和<。举例来说,Money(1,75)+Money(0,50)==Money(2,25)应为true。你应该同时提供*和/操作符吗?为什么?
class Money(d: BigInt, c: BigInt) extends Ordered[Money]{
val dollars: BigInt = d + c /100
val cents: BigInt = c % 100
override def toString() = "%d.%d".format(dollars, cents)
def toCents(): BigInt = dollars * 100 + cents
def fromCents(cents: BigInt) = new Money(cents / 100, cents % 100)
override def compare(that: Money): Int = toCents.compare(that.toCents)
def + (that: Money) = fromCents(this.toCents + that.toCents)
def - (that: Money) = fromCents(this.toCents - that.toCents)
}
object Money{
def apply(d: Int, c: Int) = new Money(d, c)
}
val x = Money(1, 75)
val y = Money(0, 50)
println(x + y)
println(x - y)
println(x > y) //true
11.5 提供操作符用于构造HTML表格。例如:Table() | "Java" | "Scala" || "Gosling" | "Odersky" || "JVM" | "JVM,.NET"应产出
:<table><tr><td>Java</td></tr><td>Scala</td></tr><tr><td>Gosling…
import collection.mutable.ArrayBuffer
class Table{
private val chunks = new ArrayBuffer[String]
def | (chunk: String)= {
chunks += "<td>%s</td>\n".format(chunk)
this
}
def ||(chunk: String) = {
chunks += "</tr><tr>\n<td>%s</td>".format(chunk)
this
}
override def toString = "<table><tr>\n%s</tr><table".format(chunks.mkString)
}
object Table{
def apply() = new Table()
}
val t = Table() | "Java" | "Scala" || "Gosling" | "Odersky" || "JVM" | "JVM,.NET"
println(t)
11.6 提供一个ASCIIArt类,其对象包含类似这样的图形:
/\_/\
( ‘ ‘ )
( - )
| | |
(__|__)
提供将两个ASCIIArt图形横向或纵向结合的操作符。选用适当优先级的操作符命名。纵向结合的实例
/\_/\ -----
( ‘ ‘ ) / Hello \
( - ) < Scala |
| | | \ Coder /
(__|__) -----
class ASCIIArt(val art: String){
def +(other: ASCIIArt) = new ASCIIArt(
art.split("\n").zip(other.art.split("\n")).map(x => x._1 + x._2).mkString("\n")
)
def ^(other: ASCIIArt) = new ASCIIArt( art + "\n" + other.art)
override def toString = art
}
val x = new ASCIIArt(
""" /\_/\
( ‘ ‘ )
( - )
| | |
(__|__)""")
val y = new ASCIIArt(
""" -----
/ Hello \
< Scala |
\ Coder /
-----""")
println(x + y)
println(x ^ y)
11.7 实现一个BigSequence类,将64个bit的序列打包在一个Long值中。提供apply和update操作来获取和设置某个具体的bit
class BitSequence(private var value: Long = 0){
implicit def bool2int(b: Boolean) = if(b) 1 else 0
def update(bit: Int, state: Int) = value |= (state & 1L)<< bit % 64
def apply(bit: Int): Int = if((value & 1L << bit % 64) > 0) 1 else 0
override def toString = "%64s".format(value.toBinaryString).replace(" ", "0")
}
val x = new BitSequence()
x(5) = 1
x(63) = 1
x(64) = 1
println(x(5))
println(x)
11.8 提供一个Matrix类—你可以选择需要的是一个2*2的矩阵,任意大小的正方形矩阵,或m*n的矩阵。支持+和操作。操作应同样适用于单值,例如mat*2。单个元素可以通过mat(row,col)得到
class Matrix(val m: Int, val n: Int){
private val value = Array.ofDim[Double](m, n)
def update(x: Int, y: Int, v: Double) = value(x)(y) = v
def apply(x: Int, y: Int) = value(x)(y)
def +(other: Matrix) = {
require (n == other.n)
require (m == other.m)
var res = new Matrix(m,n)
for(i <- 0 until m; j <- 0 until n){
res(i, j) = this.value(i)(j) + other.value(i)(j)
}
res
}
def *(factor: Double) = {
var res = new Matrix(m, n)
for(i <- 0 until m; j <- 0 until n) {
res(i, j) = this.value(i)(j) * factor
}
res
}
private def prod(other: Matrix, i: Int, j: Int) = {
(for (k <- 0 until n) yield value(i)(k) * other.value(j)(k)).sum
}
def *(other: Matrix) = {
require(n == other.m)
var res = new Matrix(m, n)
for(i <- 0 until m; j <- 0 until n) {
res(i, j) = prod(other, i, j)
}
res
}
override def toString = value.map(_.mkString(" ")).mkString("\n")
}
val x = new Matrix(2,3)
x(0, 0) = 1
x(0, 0) = 1
x(1, 0) = 3
x(1, 1) = 4
println(x)
println()
println(x * 2)
println()
11.9 为RichFile类定义unapply操作,提取文件路径,名称和扩展名。举例来说,文件/home/cay/readme.txt的路径为/home/cay,名称为readme,扩展名为txt
object RichFile{
def unapply(s: String) = {
val pos = s.lastIndexOf("/")
if(pos == -1) None else Some((s.substring(0, pos), s.substring(pos+1)))
}
}
val RichFile(path, name) = "/home/user/scala/test.txt"
println("Path: %s, File: %s".format(path, name))
11.10 为RichFile类定义一个unapplySeq,提取所有路径段。举例来说,对于/home/cay/readme.txt,你应该产出三个路径段的序列:home,cay和readme.txt
object RichFile {
def unapplySeq(s: String): Option[Seq[String]] = {
if (s.trim == "") None else Some(s.trim.split("/"))
}
}
val RichFile(first, middle, last) = "home/user/text.txt"
println("First: %s, Middle: %s, Last: %s".format(first, middle, last))