《Scala谜题》读书笔记

#scala

有些问题还没有完全搞懂,不过先记录下来。

先放一张 scala 的类图:

scala-class-relations.jpeg

笔记按照题目顺序整理。

//scalaVersion:2.12
package numericOps

import scala.collection.mutable

object ScalaPuzzlers extends App {
  /*
//  1. 占位符
//  Hi
//  Hi
//  List(2, 3)
//  常规匿名函数是从 => 一直到代码块结束的所有代码
  println(List(1, 2).map{i => println("Hi"); i + 1})
//  Hi
//  List(2, 3)
//  占位符_语法定义的匿名函数,只包括含有_的表达式
  println(List(1, 2).map{println("Hi"); _ + 1})

//  2. 初始化变量
  var MONTH = 12; var DAY = 24
//  scala 总认为首字母大写的变量为常量,因此赋值失败
//  match case 时也是如此
//  not found: value HOUR...MINUTE...SECOND
//  var (HOUR, MINUTE, SECOND) = (12, 0, 0)
//  3. 成员声明的位置
  trait A {
    val audience: String
    println("Hello " + audience)
  }

  class BMember(a: String = "World") extends A {
    val audience = a
    println("I repeat: Hello " + audience)
  }

  class BConstructor(val audience: String = "World") extends A {
    println("I repeat: Hello " + audience)
  }
//  构造器中声明的,跟A不同
//  Hello null
//  I repeat: Hello Readers
  new BMember("Readers")
//  构造体中声明的,跟A相同
//  Hello Readers
//  I repeat: Hello Readers
  new BConstructor("Readers")
//  4. 继承 ?
  trait A {
    val foo: Int
    val bar = 10
    println("In A: foo: " + foo + ", bar: " + bar)
  }

  class B extends A {
    val foo: Int = 25
    println("In B: foo: " + foo + ", bar: " + bar)
  }

  class C extends B {
    override val bar: Int = 99
    println("In c: foo: " + foo + ", bar: " + bar)
  }
//  超类会在子类之前初始化
//  按照声明顺序初始化
//  重载的变量,只初始化一次,在最大的子类里
//  A B 里 bar 值为0,因为 bar 只在 C 里初始化一次
//  In A: foo: 0, bar: 0
//  In B: foo: 25, bar: 0
//  In c: foo: 25, bar: 99
  new C()
//  如果改成 def bar,那么A B C对应的 bar 值都为 99
//  5. 集合操作
  def sumSizes(collections: Iterable[Iterable[_]]): Int =
  collections.map(_.size).sum
//  操作符一般会保持输入的集合类型不变,set去重后只有一个值
//  4
  println(sumSizes(List(Set(1, 2), List(3, 4))))
//  2
  println(sumSizes(Set(List(1, 2), Set(3, 4))))
//  6. 参数类型
  def applyMulti[T](n: Int)(arg: T, f: T => T) =
    (1 to n).foldLeft(arg) { (acc, _) => f(acc) }
  def applyNCurried[T](n: Int)(arg: T)(f: T => T) =
    (1 to n).foldLeft(arg) { (acc, _) => f(acc) }
  def nextInt(n: Int) = n * n + 1
  def nextNumber[N](n: N)(implicit numericOps: Numeric[N]) =
    numericOps.plus(numericOps.times(n, n), numericOps.one)
  println(applyMulti(3)(2, nextInt))
  println(applyNCurried(3)(2)(nextInt))
//  一个参数类型的信息,对同一个参数列表里参数的不可用。对随后的参数列表是可用的。
//  println(applyMulti(3)(2.0, nextNumber))
  println(applyNCurried(3)(2.0)(nextNumber))

//  7. 闭包
  import collection.mutable.Buffer
  val accessors1 = Buffer.empty[() => Int]
  val accessors2 = Buffer.empty[() => Int]

  val data = Seq(100, 110, 120)
  var j = 0
  for (i <- 0 until data.length) {
    accessors1 += (() => data(i))
    accessors2 += (() => data(j))
    j += 1
  }
//  在闭包里,val存储为常规的Int;而var变成一个scala.runtime.IntRef
//  因此避免在闭包里使用可变var或可变对象的自由变量
//  如果要用,先赋值给val,然后传到闭包里
//  100
//  110
//  120
  accessors1.foreach(a1 => println(a1()))
//  抛出异常:java.lang.IndexOutOfBoundsException: 3
  accessors2.foreach(a2 => println(a2()))

//  8. Map表达式
  val xs = Seq(Seq("a", "b", "c"), Seq("d", "e", "f"),
    Seq("g", "h"), Seq("i", "j", "k"))
  val ys = for (Seq(x, y, z) <- xs) yield x + y + z
//  for (pattern <- expr) yield fun
//  转换后为
//  expr withFilter {
//    case pattern => true
//    case _ => false
//  } map { case pattern => fun }
//  因此for会自动跳过不匹配的值
//  List(abc, def, ijk)
  println(ys)
//  scala.MatchError: List(g, h)
  val zs = xs map { case Seq(x, y, z) => x + y + z }
  println(zs)

//  9. 循环引用变量
  object XY {
    object X {
      val value: Int = Y.value + 1
    }
    object Y {
      val value: Int = X.value + 1
    }
  }
//  重新执行多次都是一种结果:或者 X:1 Y:2 或者 X:2 Y:1
//  实际代码尽量避免循环引用
  println(if (math.random > 0.5) XY.X.value else XY.Y.value)

//  10. 等式的例子
  import collection.immutable.HashSet
  trait TraceHashCode {
    override def hashCode(): Int = {
      println(s"TRACE: In hashCode for ${this}")
      super.hashCode()
    }
  }
  case class Country(isoCode: String)
  def newSwitzInst = new Country("CH") with TraceHashCode
  val countriesInst = HashSet(newSwitzInst)
//  true
  println(countriesInst.iterator contains newSwitzInst)
//  true
  println(countriesInst contains newSwitzInst)

  case class CountryWithTrace(isoCode: String) extends TraceHashCode
  def newSwitzDecl = new CountryWithTrace("CH")
  val countriesDecl = HashSet(newSwitzDecl)
//  true
  println(countriesDecl.iterator contains newSwitzDecl)
//  false
  println(countriesDecl contains newSwitzDecl)

//  new Country 的对象都有相同的 hashCode,相同的 equal
//  new CountryWithTrace 的对象都有不同的 hashCode,相同的 equal
//  case class 的 equal/hashCode 实现都是基于结构等式的:如果两个实例有相同的类型和相等的构造器参数
//  那么这两个实例就应该是相等的,这点跟普通class不同,普通class的两个实例一定是不相等的
//  override 之后,case class 就失去了这个特性
//  instx 有相同的 hashCode() 且 == 为 true
  val inst1 = new Country("CH") with TraceHashCode
  val inst2 = new Country("CH") with TraceHashCode
  def inst3 = new Country("CH") with TraceHashCode
  def inst4 = new Country("CH") with TraceHashCode
  //  declx 有不同的 hashCode() 且 == 为 true
  var decl1 = new CountryWithTrace("CH")
  var decl2 = new CountryWithTrace("CH")
  def decl3 = new CountryWithTrace("CH")
  def decl4 = new CountryWithTrace("CH")

//  11. lazy val
  var x = 0
  lazy val y = 1 / x
  try {
    println(s"1st $y")
  } catch {
    case _: Exception =>
      x = 1
      // lazy val 如果初始化失败的话,下次取值时还会尝试初始化,直到成功
      // 2nd 1
      println(s"2nd $y")
  }

//  12. 集合的迭代顺序
  case class RomanNumeral(symbol: String, value: Int)
  implicit object RomanOrdering extends Ordering[RomanNumeral] {
    def compare(a: RomanNumeral, b: RomanNumeral) =
      a.value compare b.value
  }
  import collection.immutable.SortedSet
  val numerals = SortedSet(
    RomanNumeral("M", 1000),
    RomanNumeral("C", 100),
    RomanNumeral("X", 10),
    RomanNumeral("I", 1),
    RomanNumeral("D", 500),
    RomanNumeral("L", 50),
    RomanNumeral("V", 5)
  )
  println("Roman numeral symbols for 1 5 10 50 100 500 1000")
//  I V X L C D M
//  C D I L M V X
  for (num <- numerals; sym = num.symbol) { print(s"${sym} ")}
  println()
//  转换会保持集合的类型不变,map后的集合仍然为set,按照 ASCII 顺序排序
//  如果想要保持原始顺序,toSeq 转为 sequence
  numerals map { _.symbol } foreach { sym => print(s"${sym} ") }
//  I V X L C D M
  numerals.toSeq map { _.symbol } foreach { sym => print(s"${sym} ") }

//  13. 自引用
//  显式给出类型的话,递归值定义是允许的,因此接下来这两行编译正常
  val s1: String = s1
//  当右值里有未初始化的变量时,赋缺省值,此处为 null
  val s2: String = s2 + s2
//  java.lang.NullPointerException
//  println(s1, s1.length)
//  (nullnull,8)
  println(s2, s2.length)
//  尽量避免自引用的形式
//  14. Return语句
val two = (x: Int) => { return x; 2 }
  def sumItUp: Int = {
    def one(x: Int): Int = { return x; 1 }
//    如果 two 直接在外部定义,则会报 return outside method definition 的错误
//    而这里之所以编译成功,是因为作为 sumItUp 的最终返回值返回了
//    因此 two 的参数是什么,返回值就是什么
    val two = (x: Int) => { return x; 2 }
    1 + one(2) + two(5)
  }
//  5
  println(sumItUp)
//  15. 偏函数中的_
  var x = 0
  def counter() = { x += 1; x}
  def add(a: Int)(b: Int) = a + b
//  adder1 扩展为 a => add(counter)(a)
//  执行 adder1 才会计算参数 counter,而且每次都会重新计算。所以等价于 def adder1 = ...
  var adder1 = add(counter)(_)
//  偏函数,adder2 扩展为
//  val adder2 = {
//    val fresh = counter()
//    a => add(fresh)(a)
//  }
//  因此 counter 会立即计算,并且之后 adder2 使用的是这个镜像,多次调用 adder2 不会重新计算 counter
  var adder2 = add(counter) _
//  def adder1
//  x = 1
  println("x = " + x)
//  12
  println(adder1(10))
//  x = 2
  println("x = " + x)
//  11
  println(adder2(10))
//  x = 2
  println("x = " + x)
//  12
  println(adder1(10))
//  x = 13
  println("x = " + x)

//  16. 多参数列表
  def invert(v3: Int)(v2: Int = 2, v1: Int = 1): Unit = {
    println(v1 + ", " + v2 + ", " + v3)
  }
  val invert3 = invert(3) _
//  not enough arguments for method apply: (v1: Int, v2: Int)Unit in trait Function2.
//  Unspecified value parameter v2.
//  invert3(v1 = 2)//1, 2, 3
  invert3(v1 = 2, v2 = 1)//1, 2, 3
  invert3(v2 = 2, v1 = 1)//2, 1, 3
  invert3(v1 = 1, v2 = 2)//2, 1, 3
//  invert3 扩展为
//  def invert3 = new Function2(Int, Int, Unit) {
//    override def apply(v1: Int, v2: Int): Unit = invert(3)(v1, v2)
//  }
//  因此后续调用 invert3(...)指定的参数名,都以扩展后的函数样式为准,比如指定的参数名
//  invert 的参数名这里只是混淆作用,invert3 指定的参数名跟 invert 的参数名是没有关系的

//  17. 隐式参数
  implicit var z1 = 2
  def add(x: Int)(y: Int)(implicit z: Int) = x + y + z
  // addTo 类型为 Int => Int = <function1>,只接收一个参数
  def addTo(n: Int) = add(n) _
  implicit val z2 = 3
  val addTo1 = addTo(1)
//  隐式参数是由编译器在方法 addTo 编译时解析的,此时只有 z1 在范围内,因此使用 z1 变量
//  5 = 1 + 2 + 2
  println(addTo1(2))
  z1 = 200
//  203 = 1 + 2 + 200
  println(addTo1(2))
//  Int does not take parameters
//  addTo1(2)(3)
//  如果声明 addTo1 时, z1 z2 都在范围内,那么确实会报 ambiguous implicit values

//  18. 重载
  object Oh {
    def overloadA(u: Unit) = "I accept a Unit"
    def overloadA(u: Unit, n: Nothing) = "I accept a Unit and Nothing"
    def overloadB(n: Unit) = "I accept a Unit"
    def overloadB(n: Nothing) = "I accept Nothing"
  }
//  代码可以运行,不过会报 Warning: a pure expression does nothing in statement position
//  I accept a Unit
//  overloadA 两个重载形状不同,编译器可以简单判断使用第一个
//  编译器应用了值抛弃,传入的参数修改为 { 99; () },不过同样会导致上述 Warning
  println(Oh overloadA 99)
//  Error: overloaded method value overloadB with alternatives
//  由于 overloadB 两个重载形状相同,因此编译器需要判断使用哪个,overloadB 两种形式都无法把 Int 作为参数,因此编译报错
//  println(Oh overloadB 99)

//  19. 命名参数和缺省参数
  class SimpleAdder {
    def add(x: Int = 1, y: Int = 2) = x + y
  }
  class AdderWithBonus extends SimpleAdder {
//    override def add(y: Int, x: Int): Int = super.add(x, y) + 10
    override def add(y: Int = 3, x: Int = 4): Int = super.add(x, y) + 10
  }
  val adder:SimpleAdder = new AdderWithBonus
//  13
//  函数参数名字使用基类的,默认值则采用子类的,这里转换为 add(_, 0)
//  第一个参数默认值为3,因此值为 3 + 0 + 10
  println(adder add (y = 0))
//  14
//  这里转换为 add(0, _)
//  第二个参数默认值为 4,因此值为 0 + 4 + 10
  println(adder add 0)
//  即使在 C++ 里,在 override 的函数里修改默认值,也是非常不建议的

//  20. 正则表达式
  def traceIt[T <: Iterator[_]](it: T) = {
    println(s"TRACE: using iterator '${it}'")
    it
  }
  val msg = "I love Scala"
//  这里跟书里结论不同
//  TRACE: using iterator '<iterator>
//  First match index:9
//  First match index:9
  println("First match index:" + traceIt("a".r.findAllIn(msg)).start)
  println("First match index:" + "a".r.findAllIn(msg).start)

//  21. 填充
  implicit class Padder(val sb: StringBuilder) extends AnyVal {
    def pad2(width: Int) = {
      1 to width - sb.length foreach { sb += '*' }
      sb
    }
  }
//  length == 14
  val greeting = new StringBuilder("Hello, kitteh! ")
//  Hello, kitteh! *
  println(greeting pad2 20)
//  length == 9
  val farewell = new StringBuilder("U go now.")
//  java.lang.StringIndexOutOfBoundsException: index 10,length 10
//  println(farewell pad2 20)
  //  如果直接这么写代码 1 to 6 foreach { println("Hi") }
  //  编译报错:
  //  type mismatch;
  //  found: Unit
  //  required: Int => ?
  //  也就是说,必须把 1 to 6 的 Int 传入并且使用
  //  不过这样会编译成功
  //  println(1 to 6 foreach { greeting }),但是什么都不会输出。
  //  原因是默认调用了 StringBuilder 的 apply(index:Int):Char 方法
  //  所以 farewell 会报下标超限的错误
  //  而 greeting 的输出结果,跟第一个例子相关,因为 "sb += '*'" 只会执行一次。

//  22. 投影
  import collection.JavaConverters._
  def javaMap:java.util.Map[String, java.lang.Integer] = {
    val map =
      new java.util.HashMap[String, java.lang.Integer]()
    map.put("key", null)
    map
  }
  val scalaMap = javaMap.asScala
  val scalaTypesMap =
    scalaMap.asInstanceOf[scala.collection.Map[String, Int]]

  println(scalaTypesMap("key") == null)//true
  println(scalaTypesMap("key") == 0) //true
  println(scalaMap("key") == null) //true
  println(scalaMap("key") == 0) //false
  println(null.asInstanceOf[Int] == 0) //true
//  23. 构造器参数
  class Printer(prompter: => Unit) {
    def print(message: String, prompted: Boolean = false): Unit = {
      if (prompted) prompter
      println(message)
    }
  }
  def prompt(): Unit = {
    print("puzzler$ ")
  }
//  puzzler$ Puzzled yet?
  new Printer { prompt } print ( message = "Puzzled yet? ")
//  puzzler$ Puzzled yet?
  new Printer { prompt } print ( message = "Puzzled yet? ",
    prompted = true)
//  实际上,上述两个语句都是转为以下形式调用的
//  new Printer(()) { prompt } print { message = "Puzzled yet? "}
//  因此都会先调用 prompt
//  使用 () 替代 {} 后,输出就是正常的了
//  Puzzled yet?
  new Printer ( prompt ) print ( message = "Puzzled yet? ")
//  puzzler$ Puzzled yet?
  new Printer ( prompt ) print ( message = "Puzzled yet? ",
    prompted = true)
//  24. Double.NaN
  def printSorted(a: Array[Double]): Unit = {
    util.Sorting.stableSort(a)
    println(a.mkString(" "))
  }
//  1.23 4.56 7.89 NaN
  printSorted(Array(7.89, Double.NaN, 1.23, 4.56))
//  1.23 4.56 7.89 NaN
  printSorted(Array(7.89, 1.23, Double.NaN, 4.56))
//  这里也跟书里结果不同,所以我觉得NaN在跟 Double 比较时行为是无法预期的
//  属于 undefined behavior
  println(Seq(1.0, Double.NaN, 1.1).max)// 1.1
  println(Seq(1.0, 1.1, Double.NaN).max)// NaN
  println(Double.NaN > 100)// false
  println(Double.NaN < 100)// false
  println(Double.NaN == 100)// false
  println(Double.NaN != 100)// true
//  25. getOrElse
  val zippedLists = (List(1, 2, 3) , List(4, 5, 6)).zipped
//  scala.MatchError: 10 (of class java.lang.Integer)
  val (x, y) = zippedLists.find(_._1 > 10).getOrElse(10)
//  Option[A].getOrElse 不一定返回类型 A:
//  final def getOrElse[B >: A](default: => B): B
//  例如
//  val a = Some(1) //a: Some[Int] = Some(1)
//  a.getOrElse(2) //res0: Int = 1
//  a.getOrElse("ufo") //res1: Any = 1
//  当参数为 2 的时候返回 Int,当参数为 string 时,返回 Int 与 String 的最直接的子类 Any,而不会报错
//  类似的,val (x, y) = ... 编译期间也不会报错,而是展开为如下形式
//  val a$ = zippedLists.find(_._1 > 10).getOrElse(10) match {
//    case (b, c) => (b, c)
//  }
//  val x = a$._1
//  val y = a$._2
//  因此在运行时由于模式匹配失败报错了
//  自然的,如果这么修改之后返回的 x 类型为 Any
//  val x = zippedLists.find(_._1 > 10).getOrElse(10)
//  x: Any = 10
//  26. Any Args
  def prependIfLong(candidate: Any, elems: Any*):Seq[Any] = {
    if (candidate.toString.length > 1)
      candidate +: elems
    else
      elems
  }
//  love
  println(prependIfLong("I", "love", "Scala")(0))
  def prependIfLongRefac(candidate: Any)(elems: Any*):Seq[Any] = {
    if (candidate.toString.length > 1)
      candidate +: elems
    else
      elems
  }
//  ArrayBuffer((I,love,Scala), 0)
  println(prependIfLongRefac("I", "love", "Scala")(0))

//  编译器把 "I", "love", "Scala" 放到元组,作为第一个参数,0则作为第二个参数
//  跟32题,自动插入一个元组()有点像
//  可以使用 -Yno-adapted-args 直接编译失败,或者 -Ywarn-adapted-args 发出编译warning.
//  27. null
  def objFromJava: Object = "string"
  def stringFromJava: String = null

  def printLengthIfString(a: AnyRef):Unit = a match {
    case str:String =>
      println(s"String of length ${str.length}")
    case _ => println("Not a string")
  }

//  String of length 6
  printLengthIfString(objFromJava)
//  Not a string
  printLengthIfString(stringFromJava)
//  Scala 类关系图:![scala-class-relations](/assets/images/scala-class-relations.jpeg)
//  Null 是 Scala 中仅仅为了与 Java 兼容而存在的特殊类型,只有一个实例:null
//  Null 是所有引用类型的子类型,因此,参数如果为引用类型,那么都可以传入 null
//  但是 null 除了 Null 不是任何其他类型的实例
//  28. AnyVal
  trait NutritionalInfo {
    type T <: AnyVal
    var value:T = _
  }
  val containsSugar = new NutritionalInfo { type T = Boolean }

//  注:跟书里不同
//  false
  println(containsSugar.value)
//  true
  println(! containsSugar.value)
//  29. 隐式变量
  object Scanner {
    trait Console { def display(item: String) }
    trait AlarmHandler extends (() => Unit)

    def scanItem(item: String)(implicit c: Console): Unit = {
      c.display(item)
    }

    def hitAlarmButton()(implicit ah:AlarmHandler) { ah() }
  }
  object NormalMode {
    implicit val ConsoleRenderer = new Scanner.Console {
      def display(item: String) { println(s"Found a ${item}") }
    }
    implicit val DefaultAlarmHandler = new Scanner.AlarmHandler {
      def apply() { println("ALARM! ALARM!")}
    }
  }
  object TestMode {
    implicit val ConsoleRenderer = new Scanner.Console {
      def display(item: String) { println("Found a detonator") }
    }
    implicit val TestAlarmHandler = new Scanner.AlarmHandler {
      def apply() { println("Test successful. well done! ") }
    }
  }

  import NormalMode._
//  Found a knife
  Scanner scanItem "knife"
//  ALARM! ALARM!
  Scanner.hitAlarmButton()

  import TestMode._
//  跟书里结果不同,待搞明白后再补充解释
//  明确的点是尽量不要使用同名的隐式变量
//  Scanner scanItem "shoe"
//  Scanner.hitAlarmButton()
//  30. 显式声明类型?
  class QuietType {
    implicit val stringToInt = (_:String).toInt
    println("4" - 2)
  }
  class OutspokenType {
    implicit val stringToInt: String => Int = _.toInt
    println("4" - 2)
  }
//  2
  new QuietType()
//  java.lang.StackOverflowError
  new OutspokenType()
//  31. View
  val ints = Map("15" -> List(1, 2, 3, 4, 5))
  val intsIter1 = ints map { case (k, v) => (k, v.toIterator) }
  val intsIter2 = ints mapValues(_.toIterator)
  //1, 2
  println(intsIter1("15").next, intsIter1("15").next)
  //1, 1
  println(intsIter2("15").next, intsIter2("15").next)
//  32. toSet
//  def toSet[B >: A]: Set[B]
//  Set(1, 2, 3)
  println(List("1", "2").toSet + "3")
//  toSet 后的(),解释成 apply 方法: def apply(elem: A): Boolean,查看元素是否存在
//  而不传递参数值时,则认为是Unit值,一个(),例如这样:
//  def foo(x: Any): Unit = {
//    println(s"Hi ${x}")
//  }
//
//  foo(1)
//  foo("World")
//  foo()
//  false3
//  ()不存在于该 Set,因此返回 false
  println(List("1", "2").toSet() + "3")
//  true3
  println(List("1", "2").toSet("1") + "3")
//  33. 缺省值
//  这个例子的结果也和书上不同,不过表达的主题是一样的:withDefaultValue 不同实例使用的是同一个缺失值对象
//  这个例子会更直观一些
//  val a = mutable.HashMap[String, mutable.Map[Int, String]]()
//            .withDefaultValue(mutable.Map[Int, String]())
//  a("id1")(2) = "three"
//  //  three
//  println(a("id1")(2))
//  //  three
//  println(a("id2")(2))
  import collection.mutable
  import collection.mutable.Buffer

  val accBalances: mutable.Map[String, Int] =
    mutable.Map() withDefaultValue 100
  def transaction(accHolder: String,
                  amount: Int,
                  accounts: mutable.Map[String, Int]): Unit = {
    accounts += accHolder -> (accounts(accHolder) + amount)
  }

  def accBalancesWithHist: mutable.Map[String, Buffer[Int]] =
    mutable.Map() withDefaultValue Buffer(100)
  def transactionWithHist(accHolder: String,
                          amount: Int,
                          accounts: mutable.Map[String, Buffer[Int]]): Unit = {
    val newAmount = accounts(accHolder).head + amount
    accounts += accHolder ->
      (newAmount +=: accounts(accHolder))
  }
  transaction("Alice", 100, accBalances)
//  200
  println(accBalances("Alice"))
//  100
  println(accBalances("Bob"))

  transactionWithHist("Dave", 100, accBalancesWithHist)
  println(accBalancesWithHist)
  println(accBalancesWithHist("Dave"))
  println(accBalancesWithHist("Carol"))
  println(accBalancesWithHist("Dave"))

//  34. 关于Main
  class AirportDay {
    def tryCheckBag(weight: Int): String =
      "It's not a full flight, Your bag is OK."
  }
  class StartOfVacation extends AirportDay {
    override def tryCheckBag(weight: Int): String =
      if (weight > 25)
        "Your bag is too heavy.Please repack it."
      else
        "Your bag is OK."
  }
  def goToCheckIn(bagWeight: Int)(implicit ad:AirportDay): Unit = {
    println(s"The agent says:${ad tryCheckBag bagWeight}")
  }
  object AirportSim {
    def main(args: Array[String]) = {
      implicit val quietTuesday = new AirportDay
      goToCheckIn(26)
      implicit val busyMonday = new StartOfVacation
      goToCheckIn(26)
    }
  }
  object AirportSim2 extends App {
    implicit val quietTuesday = new AirportDay
    goToCheckIn(26)
    implicit val busyMonday = new StartOfVacation
    goToCheckIn(26)
  }

//  跟书里结论不同
//  The agent says:It's not a full flight, Your bag is OK.
//  The agent says:Your bag is too heavy.Please repack it.
//  The agent says:It's not a full flight, Your bag is OK.
//  The agent says:Your bag is too heavy.Please repack it.
  AirportSim main Array()
  AirportSim2 main Array()

//  35. 列表
  type Dollar = Int
  final val Dollar:Dollar = 1
  var x:List[Dollar] = List(1, 2, 3)
//  大括号内翻译为 (x:Int) => Dollar
//  语句里第一个 x 指列表,第二个 x 表示一个传递给 map 函数的参数
//  List(1, 1, 1)
  println(x map { x: Int => Dollar })
//  java.lang.IndexOutOfBoundsException: 3
//  小括号内翻译为 x: (Int => Dollar)
//  语句里两个 x 都表示列表,因此报range的错误
//  如果将 x 值修改为 List(0, 1, 2),则"侥幸"运行通过,输出List(0, 1, 2)
//  println(x map ( x: Int => Dollar ))
//  如果没有传递参数类型,也可以运行输出 List(1, 1, 1)
  println(x map ( x => Dollar ))
//  如果使用圆括号传递一个参数的匿名函数,而这个匿名函数含有类型申明,那参数一定要放到圆括号里
//  println(x map ( (x: Int) => Dollar ))
//  36. 计算集合的大小
  import collection.mutable
  def howManyItems(lunchbox: mutable.Set[String],
                   itemToAdd: String): Int = (lunchbox + itemToAdd).size
  def howManyItemsRefac(lunchbox: mutable.Iterable[String],
                        itemToAdd: String): Int = (lunchbox + itemToAdd).size
  val lunchbox =
    mutable.Set("chocolate bar", "orange juice", "sandwich")
//  4
//  Set(sandwich, chocolate bar, orange juice, apple)
  println(howManyItems(lunchbox, "apple"))
  println(lunchbox + "apple")
//  Iterable 不支持 +,这里实际上是先通过 Predef.any2stringadd 转换为 string
//  47
//  ArrayBuffer(Set(sandwich, chocolate bar, orange juice))apple
  println(howManyItemsRefac(lunchbox, "apple"))
  println(mutable.Iterable(lunchbox) + "apple")
//  3
//  + 创建了一个新的 set,因此大小仍然是3。修改集合使用 +=
  println(lunchbox.size)
   */
}