快学Scala 第13章 集合 - 练习解答

1. 编写一个函数,给定字符串,产出一个包含所有字符的下标的映射。举例来说:indexes("Mississippi")应返回一个映射,让‘M‘对应集{0},‘i‘对应集{1,4,7,10},依此类推。

使用字符到可变集的映射。另外,你如何保证集是经过排序的?

回答:使用SortedSet可以保证集是经过排序的。

package ex13_01

import scala.collection.mutable.SortedSet
import scala.collection.mutable.Map

object Main extends App{

  def indexes(s: String): Map[Char,SortedSet[Int]]={
    val map = Map[Char,SortedSet[Int]]()
    for(i <- 0 until s.length()){
      //map  += s(i).toChar -> (map.getOrElse(s(i).toChar, SortedSet(i)) += i)
      map(s(i).toChar) = (map.getOrElse(s(i).toChar, SortedSet(i)) += i)
    }
    map
  }

val map = indexes("Mississippi")
println(map.mkString(","))

}
/* output:
M -> TreeSet(0),s -> TreeSet(2, 3, 5, 6),p -> TreeSet(8, 9),i -> TreeSet(1, 4, 7, 10)
*/

2. 重复前一个练习,这次用字符到列表的不可变映射。

package ex13_02

object Main extends App {

  def indexes(s: String): Map[Char, List[Int]] = {
    var map = Map[Char, List[Int]]()
    for (i <- 0 until s.length()) {
      val templist = map.get(s(i).toChar)
      if (templist == None) map += s(i).toChar -> List(i)
      else if (!templist.contains(i)) map += s(i).toChar -> (templist.get ::: List(i))

    }
    map
  }

  val map = indexes("Mississippi")
  println(map.mkString(","))

}
/* output:
M -> List(0),i -> List(1, 4, 7, 10),s -> List(2, 3, 5, 6),p -> List(8, 9)
*/

补充阅读:
http://stackoverflow.com/questions/27557427/scala-2-11-linkedlist-is-deprecated-what-should-i-use

3. 编写一个函数,从一个整型链表中去除所有零值。

package ex13_03
import scala.collection.mutable.MutableList
object Main extends App {
  def removeZero(lst: MutableList[Int]):MutableList[Int]={
    val mylist = lst.filter( _ != 0)
    mylist
  }
  val a = MutableList(1,2,3,0,5,0,7)
  println(a)
  println(removeZero(a))
}
/* output:
MutableList(1, 2, 3, 0, 5, 0, 7)
MutableList(1, 2, 3, 5, 7)
*/

4. 编写一个函数,接受一个字符串的集合,以及一个从字符串到整数值的映射。返回整型的集合,其值为能和集合中某个字符串相对应的映射的值。举例来说,

给定Array("Tom", "Fred", "Harry")和Map("Tom"->3,"Dick"->4,"Harry"->5),返回Array(3,5)。提示:用flatMap将get返回的Option值组合在一起。

package ex13_04
import scala.collection.mutable.MutableList
object Main extends App {
  def getScores(arr: Array[String], map: Map[String, Int]): Array[Int] = {
    val scores = arr.flatMap(map.get(_))
    scores
  }
  val arr = Array("Tom", "Fred", "Harry")
  val map = Map("Tom" -> 3, "Dick" -> 4, "Harry" -> 5)
  val scores = getScores(arr, map)
  println(scores.mkString(","))
}
/* output:
3,5
*/

5. 实现一个函数,作用与mkString相同,使用reduceLeft。

package ex13_05
import scala.collection.mutable.MutableList
object Main extends App {
  def mkString(arr: Array[String], left: String, mid: String, right: String): String = {
    val sb = new StringBuilder()
    sb.append(left)
    sb.append(arr.reduceLeft("%s%s%s".format(_, mid, _)))
    sb.append(right)
    sb.toString()
  }
  val arr = Array("Jonathan", "will", "become", "a", "billionaire", "in", "five", "years")
  println(mkString(arr, "-*-", " ", "-*-"))
}
/* output:
-*-Jonathan will become a billionaire in five years-*-
*/

6. 给定整型列表lst,(lst :\ List[Int]())(_ :: _)得到什么?(List[Int]() /: lst)(_ :+ _)又得到什么?如何修改它们中的一个,以对原列表进行反向排列?

package ex13_06
import scala.collection.mutable.MutableList
//def ::(x: A): List[A]
//[user case] Adds an element at the beginning of this list.
//def :+(elem: A): List[A]
//[use case] A copy of the list with an element appended.
object Main extends App {
  val lst = List(1,2,3,4,5,6,7)
  println(lst)

  val a = (lst :\ List[Int]())(_ :: _)  // equivalent to  (lst :\ List[Int]())(_ +: _)
  println(a)
  val b = (List[Int]() /: lst)(_ :+ _)
  println(b)

  //to reverse
  val c = (lst :\ List[Int]())( (e,r) => r :+ e )
  println(c)

  val d = (List[Int]() /: lst)( (r,e) => e +: r )
  println(d)
}
/* output:
List(1, 2, 3, 4, 5, 6, 7)
List(1, 2, 3, 4, 5, 6, 7)
List(1, 2, 3, 4, 5, 6, 7)
List(7, 6, 5, 4, 3, 2, 1)
List(7, 6, 5, 4, 3, 2, 1)
*/

7. 在13.11节中,表达式(prices zip quantities) map { p => p._1 * p._2}有些不够优雅。我们不能用(prices zip quantities) map { _ * _},因为 _ * _ 是一个带两个参数的函数,而我们需要的是一个带单个类型为元组的参数的函数,Function对象的tupled方法可以将带两个参数的函数改为以元组为参数的函数。将tupled应用于乘法函数,以使我们可以用它来映射由对偶组成的列表。

package ex13_07
import scala.collection.mutable.MutableList
object Main extends App {
  val prices = List(5.0, 20.0, 9.95)
  val quantities = List(10, 2, 1)
  println((prices zip quantities) map { pair => pair._1 * pair._2 })
  println((prices zip quantities) map { Function.tupled(_ * _) })
}
/* output:
List(50.0, 40.0, 9.95)
List(50.0, 40.0, 9.95)
*/

8. 编写一个函数。将Double数组转换成二维数组。传入列数作为参数。举例来说,Array(1,2,3,4,5,6)和三列,返回Array(Array(1,2,3),Array(4,5,6))。用grouped方法。

package ex13_08
import scala.collection.mutable.MutableList
object Main extends App {
  def to2DArray(arr: Array[Int], cols: Int): Array[Array[Int]] = {
    var result = Array[Array[Int]]()
    for (i <- arr.grouped(3)) result = result :+ i
    result
  }
  val arr = Array(1, 2, 3, 4, 5, 6)
  val result = to2DArray(arr, 3)
  result.foreach(it => println(it.mkString("[", ",", "]")))
}
/* output:
[1,2,3]
[4,5,6]
*/

9. Harry Hacker写了一个从命令行接受一系列文件名的程序。对每个文件名,他都启动一个新的线程来读取文件内容并更新一个字母出现频率映射,声明为:


val frequencies = new scala.collection.multable.HashMap[Char,Int] with scala.collection.mutable.SynchronizedMap[Char,Int]

当读到字母c时,他调用


frequencies(c) = frequencies.getOrElse(c,0) + 1

为什么这样做得不到正确答案?如果他用如下方式实现呢:


import scala.collection.JavaConversions.asScalaConcurrentMap

val frequencies:scala.collection.mutable.ConcurrentMap[Char,Int] = new java.util.concurrent.ConcurrentHashMap[Char,Int]

回答: 并发问题,并发修改集合不安全.修改后的代码和修改前的代码没有什么太大的区别.

10. Harry Hacker把文件读取到字符串中,然后想对字符串的不同部分用并行集合来并发地更新字母出现频率映射。他用了如下代码:


val frequencies = new scala.collection.mutable.HashMap[Char,Int]

for(c <- str.par) frequencies(c) = frequencies.getOrElse(c,0) + 1

为什么说这个想法很糟糕?要真正地并行化这个计算,他应该怎么做呢?(提示:用aggregate)

回答: 并行修改共享变量,结果无法估计。

package ex13_10_a
import scala.collection.immutable.HashMap
//import scala.collection.mutable.HashMap
//type mismatch; found : scala.collection.mutable.Map[Char,Int] required: scala.collection.mutable.HashMap[Char,Int]
//type mismatch; found : scala.collection.mutable.Map[Char,Int] required: scala.collection.mutable.HashMap[Char,Int]
//Implicit conversion found: c ? ArrowAssoc(c): ArrowAssoc[Char]
object Main extends App {
  def frequence(str: String): HashMap[Char, Int] = {
    val result = str.par.aggregate(HashMap[Char, Int]())(
        {
          (m, c) =>
            m + ( c -> (m.getOrElse(c,0) + 1) )
        },
        {
          (m1, m2) =>
          (m1.keySet ++ m2.keySet).foldLeft( HashMap[Char, Int]() ) {
            (result, k) =>
            result + (k -> (m1.getOrElse(k, 0) + m2.getOrElse(k, 0)))
          }
    })
    result
  }
  //val str = "You were asking me that how deep I‘ve been loving you."
  val str = "Hello"
  println(frequence(str))
}
/* output:
Map(e -> 1, l -> 2, H -> 1, o -> 1)
*/

时间: 2024-10-14 01:34:55

快学Scala 第13章 集合 - 练习解答的相关文章

快学Scala第13章----集合

本章要点 所有集合都扩展自Iterable特质 集合有三大类:序列.集.映射 对于几乎所有集合类,Scala都同时提供了可变的和不可变的版本 Scala列表要么是空的,要么拥有一头一尾,其中尾部本身又是一个列表 集是无先后次序的集合 用LinkedhashSet 来保留插入顺序,或者用SortedSet来按顺序进行迭代 '+' 将元素添加到无先后次序的集合中: +: 和 :+ 向前或向后追加到序列: ++将两个集合串接在一起: -和–移除元素 Iterable和Seq特质有数十个用于常见操作的方

快学Scala 第17章 - 类型参数 习题解答

1. 定义一个不可变类Pair[T,S],带一个swap方法,返回组件交换过位置的新对偶. package ex17_01 object Main extends App {   val p = new Pair(97 -> 'a')   val a = p.swap   println(a) } class Pair[T, S](val p: (T, S)) {   def swap = {     (p._2, p._1)   } } /*output: (a,97) */ 2. 定义一个可

快学scala 第十一章 操作符 读书笔记及习题答案代码

chapter 11 操作符 标签:快学scala 一.笔记 scala种可以在反引号中包含几乎任何字符序列, val 'val' = 42 所有的操作符都是左结合的,除了以冒号(:)结尾的操作符,和赋值操作符.用于构造列表的::操作符是又结合的.1::2::Ni1的意思是1::(2::Ni1),先创建出包含2的列表,这个列表又被作为尾巴拼接到以1作为头部的列表中. 2. 函数调用语法:f(arg1, arg2,...)扩展到可以应用于函数之外的值,如果f不是函数或方法,那么这个表达式等于f.a

快学Scala第2章–控制结构和函数 笔记

条件表达式 在Scala中,if/else 表达式是有值的,这个就是跟在if或者else之后的表达式的值.例如: val s = if(x > 0) 1 else -1 // 类似于 var s = 0 if(x > 0) s = 1 else s = -1 Scala允许使用混合类型的返回值,例如: if(x > 0) "positive" else -1 上式表达式返回的类型是它们类型的公共超类型, 在这里java.lang.String 和 Int 它们的公共超

快学Scala第10章----特质

本章要点 类可以实现任意数量的特质 特质可以要求实现它们的类具备特定的字段.方法或超类 和Java接口不同,Scala特质可以提供方法和字段的实现 当你将多个特质叠加在一起时,顺序很重要--其方法先被执行的特质排在更后面 为什么没有多重继承 Scala和Java一样不允许类从多个超类继承:从多了超类继承可能会导致许多问题,例如两个超类有相同的方法,子类该如何使用和菱形继承.在java 中类只能扩展自一个超类,它可以实现任意数量的接口,但接口只能包含抽象方法,不能包含字段. Scala提供了特质(

快学Scala第14章----模式匹配和样例类

本章要点 match表达式是一个更好的switch,不会有意外掉入到下一个分支的问题. 如果没有模式能够匹配,会抛出MatchError.可以用case _ 模式来避免. 模式可以包含一个随意定义的条件,称作守卫. 你可以对表达式的类型进行匹配:优先选择模式匹配而不是isInstanceOf/asInstanceOf. 你可以匹配数组.元组和样例类的模式,然后将匹配到的不同部分绑定到变量. 在for表达式中,不能匹配的情况会被安静的跳过. 样例类继承层级中的公共超类应该是sealed的. 用Op

快学Scala 第6章 对象 - 练习

1. 编写一个Conversions对象,加入inchesToCentimeters.gallonsToLiters和milesToKilometers方法. object Conversions {     def main(args: Array[String]){         printf("1 inch = %g centimeters\n", inchesToCentimeters(1))         printf("2 gallons = %g liter

快学Scala 第18章 高级类型 习题解答

1. 实现一个Bug类,对沿着水平线爬行的虫子建模.move方法向当前方向移动,turn方法让虫子转身,show方法打印出当前的位置.让这些方法可以被串接调用.例如: bugsy.move(4).show().move(6).show().turn().move(5).show() 上述代码应显示 4 10 5. package ex18_01 class Bug {   var x = 0   var y = 0   var curr_direction = 0   def move(len:

快学Scala 第16章 XML处理 习题解答

1. <fred/>(0) 得到什么?<fred/>(0)(0)呢?为什么? 回答:<fred/>(0) 得到一个scala.xml.Node,<fred/>(0)(0)也是得到scala.xml.Node. 因为scala.xml.Node 实现了方法 def apply(i: Int): Node,所以支持串接调用. 注意:scala-xml-x.x.x.jar 需要另外导入. scala> val a = <fred/> a: sca