(图片来源于网络,侵删)
一、包和引入
【1】练习使用包的各种声明方式,并查看他们的不同
【2】编写一段程序,将Java哈希映射中的所有元素拷贝到Scala哈希映射。用引入语句重命名这两个类
object Q6 extends App{
import java.util.{HashMap => JavaHashMap}
import collection.mutable.{HashMap => ScalaHashMap, Map => ScalaMap}
val javaMap = new JavaHashMap[Int,String]
javaMap.put(1, "One");
javaMap.put(2, "Two");
javaMap.put(3, "Three");
javaMap.put(4, "Four");
val scalaMap = new ScalaHashMap[Int,String]
for(key <- javaMap.keySet().toArray){
scalaMap += (key.asInstanceOf[Int] -> javaMap.get(key))
}
println(scalaMap.mkString(" "))
}
二、继承
【1】扩展如下的BankAccount类,新类CheckingAccount对每次存款和取款都收取1美元的手续费
class BankAccount(initialBalance:Double){
private var balance = initialBalance
def deposit(amount:Double) = { balance += amount; balance}
def withdraw(amount:Double) = {balance -= amount; balance}
}
class BankAccount(initialBalance:Double){
private var balance = initialBalance
def deposit(amount:Double) = { balance += amount; balance}
def withdraw(amount:Double) = {balance -= amount; balance}
}
class CheckingAccount(initialBanlance:Double) extends BankAccount(initialBanlance){
override def deposit(amount:Double) = super.deposit(amount-1)
override def withdraw(amount:Double) = super.withdraw(amount+1)
}
【2】扩展前一个练习的BankAccount类,新类SavingsAccount每个月都有利息产生(earnMonthlyInterest方法被调用),并且有每月三次免手续费的存款或取款。在earnMonthlyInterest方法中重置交易计数
class BankAccount(initialBalance:Double){
private var balance = initialBalance
def deposit(amount:Double) = { balance += amount; balance}
def withdraw(amount:Double) = {balance -= amount; balance}
}
class SavingsAccount(initialBalance:Double) extends BankAccount(initialBalance){
private var num:Int = _
def earnMonthlyInterest()={
num = 3
super.deposit(1)
}
override def deposit(amount: Double): Double = {
num -= 1
if(num < 0) super.deposit(amount - 1) else super.deposit(amount)
}
override def withdraw(amount: Double): Double = {
num -= 1
if (num < 0) super.withdraw(amount + 1) else super.withdraw(amount)
}
}
【3】定义一个抽象类Item,加入方法price和description。SimpleItem是一个在构造器中给出价格和描述的物件。利用val可以重写def这个事实。Bundle是一个可以包含其他物件的物件。其价格是打包中所有物件的价格之和。同时提供一个将物件添加到打包当中的机制,以及一个适合的description方法
import collection.mutable.ArrayBuffer
abstract class Item{
def price():Double
def description():String
override def toString():String={
"description:" + description() + " price:" + price()
}
}
class SimpleItem(val price:Double,val description:String) extends Item{
}
class Bundle extends Item{
val items = new ArrayBuffer[Item]()
def addItem(item:Item){
items += item
}
def price(): Double = {
var total = 0d
items.foreach(total += _.price())
total
}
def description(): String = {
items.mkString(" ")
}
}
【4】设计一个Point类,其x和y坐标可以通过构造器提供。提供一个子类LabeledPoint,其构造器接受一个标签值和x,y坐标,比如:new LabeledPoint(“Black Thursday”,1929,230.07)
class Point(x:Double, y:Double)
class LabeledPoint(x:Double, y:Double, tag:String) extends Point(x,y)
【5】定义一个抽象类Shape,一个抽象方法centerPoint,以及该抽象类的子类Rectangle和Circle。为子类提供合适的构造器,并重写centerPoint方法
abstract class Shape{
def centerPoint()
}
class Rectangle(startX:Int,startY:Int,endX:Int,endY:Int) extends Shape{
def centerPoint() {}
}
class Circle(x:Int,y:Int,radius:Double) extends Shape{
def centerPoint() {}
}
【6】提供一个Square类,扩展自java.awt.Rectangle并且是三个构造器:一个以给定的端点和宽度构造正方形,一个以(0,0)为端点和给定的宽度构造正方形,一个以(0,0)为端点,0为宽度构造正方形
import java.awt.{Point, Rectangle}
class Square(point:Point,width:Int) extends Rectangle(point.x,point.y,width,width){
def this(){
this(new Point(0,0),0)
}
def this(width:Int){
this(new Point(0,0),width)
}
}
三、特质
【1】java.awt.Rectangle类有两个很有用的方法translate和grow,但可惜的是像java.awt.geom.Ellipse2D这样的类没有。在Scala中,你可以解决掉这个问题。定义一个RenctangleLike特质,加入具体的translate和grow方法。提供任何你需要用来实现的抽象方法,以便你可以像如下代码这样混入该特质:
val egg = new java.awt.geom.Ellipse2D.Double(5,10,20,30) with RectangleLike
egg.translate(10,-10)
egg.grow(10,20)
import java.awt.geom.Ellipse2D
trait RectangleLike{
this:Ellipse2D.Double=>
def translate(x:Double,y:Double){
this.x = x
this.y = y
}
def grow(x:Double,y:Double){
this.x += x
this.y += y
}
}
object Test extends App{
val egg = new Ellipse2D.Double(5,10,20,30) with RectangleLike
println("x = " + egg.getX + " y = " + egg.getY)
egg.translate(10,-10)
println("x = " + egg.getX + " y = " + egg.getY)
egg.grow(10,20)
println("x = " + egg.getX + " y = " + egg.getY)
}
四、类型参数
【1】定义一个不可变类Pair[T,S], 带一个swap方法,返回组件交换过位置的新对偶
class Pair[T,S](val t:T,val s:S){
def swap() = new Pair(s,t)
}
【2】定义一个可变类Pair[T],带一个swap方法,交换对偶中组件的位置
class Pair[T](val s:T,val t:T){
def swap() = new Pair(t,s)
}
【3】给定类Pair[T, S] ,编写一个泛型方法swap,接受对偶作为参数并返回组件交换过位置的新对偶。
class Pair[T,S](val t:T, val s:S){
def swap[T,S](t:T,s:S) = new Pair(s,t)
}
【4】编写一个泛型方法middle,返回任何Iterable[T]的中间元素。举例来说,middle(“World”)应得到’r’。
def middle[T](iter:Iterable[T]):T={
val seq = iter.toArray
seq(seq.length/2)
}
【5】给定可变类Pair[S,T],使用类型约束定义一个swap方法,当类型参数相同时可以被调用。
class Pair[S,T](val s:S, val t:T){
def swap(implicit env: S =:= T) = new Pair(t,s)
}
五、文件和正则表达式
【1】编写一小段Scala代码,将某个文件中的行倒转顺序(将最后一行作为第一行,依此类推)
val path = "./exercise01.txt"
val file = Source.fromFile(path)
val reverseLines = file.getLines().toArray.reverse
val pw = new PrintWriter(path)
reverseLines.foreach (line => pw.write(line+"\n"))
pw.close()
【2】编写Scala程序打印出某个网页中所有img标签的src属性。使用正则表达式和分组
val pattern = """<img[^>]+(src\s*=\s*"[^>^"]+")[^>]*>""".r
val source = scala.io.Source.fromURL("http://www.vernonzheng.com","utf-8").mkString
for (pattern(str) <- pattern.findAllIn(source)) println(str)
六、高级类型
【1】实现一个Bug类,对沿着水平线爬行的虫子建模。move方法向当前方向移动,turn方法让虫子转身,show方法打印出当前的位置。让这些方法可以被串接调用。例如:
bugsy.move(4).show().move(6).show().turn().move(5).show()
上述代码应显示4 10 5
package _1801 {
class Bug(var pos: Int = 0) {
var forword: Int = 1
def move(up: Int):this.type = {
pos += forword * up
this
}
def show():this.type = {
print(pos + " ")
this
}
def turn():this.type = {
forword = -forword
this
}
}
class Test extends App {
val bugsy = new Bug
bugsy.move(4).show().move(6).show().turn().move(5).show()
}
}
【2】为前一个练习中的Bug类提供一个流利接口,达到能编写如下代码的效果:
bugsy move 4 and show and then move 6 and show turn around move 5 and show
package _1802 {
//非动词 non-verb
object then
object show
object around
class Bug(var pos: Int = 0) {
var forword: Int = 1
def move(num: Int): this.type = { pos += num; this }
def and(obj: then.type): this.type = this
def and(obj: show.type): this.type = { print(pos + " "); this}
def turn(obj: around.type): this.type = { pos = 0; this}
}
class Test extends App {
val bugsy = new Bug
bugsy move 4 and show and then move 6 and show turn around move 5 and show
}
}
【3】实现一个方法,接受任何具备如下方法的类的对象和一个处理该对象的函数。
调用该函数,并在完成或有任何异常发生时调用close方法
def close(): Unit
def tryWithClose[T<:{def close():Unit}](obj:T,func: T => Unit)={
try{
func(obj)
}finally {
obj.close()
}
}
【4】编写一个函数printValues,带有三个参数f、from和to,打印出所有给定区间范围内的输入值经过f计算后的结果。这里的f应该是任何带有接受Int产出Int的apply方法的对象。例如:
printValues((x: Int) => x*x, 3, 6) //将打印 9 16 25 36
printValues(Array(1, 1, 2, 3, 5, 8, 13, 21, 34, 55), 3, 6) //将打印 3 5 8 13
def printValues(f:{def apply(param:Int):Int}, from:Int, to:Int)={
for(i <- from to to) {
print(f.apply(i) + " ")
}
}
printValues((x: Int) => x*x, 3, 6) //将打印 9 16 25 36
printValues(Array(1, 1, 2, 3, 5, 8, 13, 21, 34, 55), 3, 6) //将打印 3 5 8 13