scala读书笔记2

谜题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:")
for (num <- numerals; sym = num.symbol) { print(s"${sym} ") }
numerals map { _.symbol } foreach { sym => print(s"${sym} ") }

执行结果:
Prints:
I V X L C D M
C D I L M V X
1、根据scala规范,将简单的 for (i <- expr) { fun(i) }循环提糖成
foreach方法调用是expr foreach { i => fun ( i) }

2、为了能将 nurn syrn 传递进循环体,编译器在生成器中将 nume rals
替换成了元组集合( nurn, syrn )。然后在这个元组集合上调用 foreach 并在将
们传递给实际循环体之前从元组中抽取这两个元素。
第二个语句可以等价写为:
numerals map { num =>
    val sym = num.symbol
    (num,sym)
} foreach { case (num ,sym) => 
  println(sym)
}  

3、Scala 集合的一个主要特征是转换会保持合的类型不变,如 map 。在这个例子里,结果就不会是与原始集合有着相同迭代
顺序的任意集合,而是 个新的排序集 SortedSet 这个新的 SortedSet代顺序由它的元素来决定,而不是由原始集合中的元素顺序决定。
因为元组首先按它们的第一个元素来排 ,它们只是一系列 numeral
素, numerals的迭代顺序和元组的排序集是相同的。其结果就是:按期望的顺
序打印出标识符,比如,按值的升序顺序。
然而,第二个语句中所创建的符号集合是一个字符串的排序集合,因此就是
按字典顺序排序的。由于这个原因,第二个语句就是按照标识符的字母顺序打印,而不是按照期望的值增加的顺序

4、foreach 方法调用是 expr foreach { i => fun ( i) }
由于RomanNumeral是按它们的值排序的,因此遍历已排序的数字集将以该顺序返回它们,并导致根据值打印表示。
将数字映射到它们的符号将产生一组有序的String,很自然地,它们将按字典顺序排列。因此,迭代它们将按该顺序返回它们。
例如,可以通过以下方式防止对可迭代项中的项目进行潜在的意外重新排序
numerals.view map { _.symbol } foreach { ... }
or
numerals.toSeq map { _.symbol } foreach { ... }
请注意,集合的初始声明顺序对迭代顺序没有任何影响。另外,请注意,当然,使用map的代码示例会低效地迭代数字 两次:
numerals foreach { num => print(s"${num.symbol} ") }
效率更高,并以预期的顺序打印值IVXLCDM。

5、避免中间的重排序,可以采用视图
在视图上调用map不会创建一个中间的排序集,而是以lazily的方式应用
num => num.symbol,只有当要转换的numerals集合的下一个元素被检索
时才打印在 foreac 循环中
视图的法代顺序与原始集合的顺序是一致的,所以就以我们期望的顺序打印
出标识符来。在scala2.11中对视图的使用仍然有极大的争议。一个简单的替代方法是从
个转换后不影响选代顺序的集合类型开始。可以修改为如下:
numerals map.toSeq { _.symbol } foreach { sym => print(s"${sym} ") }
6、如果你正在一个集合上进行转换,尤其是当要串起多个操作时,注意:原始集合的迭代顺序可能会发
生改变。如果你需要稳定不变的迭代顺序,就将原始集合转换成sequence。
 
谜题13:
object Lives {
  class Private {
    def foo1: Any = new Private.C1
    def foo2: Any = new Private.C2
  }

  object Private {
    class C1 private {}
    private class C2 {}
  }
}
执行结果:
The compiler reports an error in the implementation of method foo1

Object Private定义了两个类C1和C2。 C2是私有类,只能在对象Private及其伴生类中访问,而C1是具有私有默认构造函数的公共类,因此该类只能在类C1中实例化。
结果,方法foo2编译得很好(因为私有类C2在类Private中可见),而方法foo1的实现报告以下编译器错误:

谜题14:
val s1: String = s1
val s2: String = s2 + s2
println(s1.length)
println(s2.length)
执行结果:
The first statement throws a NullPointerExceptionat runtime and the second prints:
8
解释:
1、根据scala的语言规范,递归值定义在scala中的确实有效的唯一条件是,必须要显式给出这个值的类型

2、值s1和s2的定义是有效的并且编译没有错误。由于定义是递归的,因此必须提供声明值的类型(SLS §4.1)。
在 Scala 中,对象字段被预初始化为其默认值,对于String类型 (以及所有其他引用类型),默认值为null。
这就像在 Java 中一样。但是,Java 类主体中字段的这种递归定义会产生非法的前向引用 编译器错误:
class Z {
   String s = s + s;
}
但以下编译正常(Java 中的自引用检查并不是非常成熟,很容易将编译器骗过去):
class Z {
   String s = this.s + this.s;
}
这表明,Java 编译器所做的检查是相当浅的。Scala 接受所有情况的递归定义的方式更加一致。
剩下要解释的是这个谜题的结果。
值s1初始化为null,因此表达式s1.length在运行时 以NullPointerException结束。
值s2的初始化实际上被转换为等效于的字节码
s2 = (new StringBuilder()).append(s2).append(s2).toString()
其中参数s2的默认值为null。根据 JLS,第 15.18.1.1 节 ,引用null被转换为字符串"null",分配给s2的值是长度为 8 的字符串"nullnull"。
3、避免定义自引用变量:用变量类型代替程序中出现的定义了支量缺省值的变量

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值