├── .gitignore ├── README.md ├── build.sbt ├── project ├── build.properties └── plugins.sbt └── src ├── main ├── java │ ├── Chapter02Task4.java │ ├── Chapter05Car.java │ ├── Chapter15Task4.java │ └── Chapter15Task5.java ├── resources │ ├── Chapter16Task04.html │ ├── Chapter16Task10.xhtml │ ├── FPinScalaCover.png │ ├── myfile.txt │ └── scaladays.png └── scala │ ├── Chapter01.scala │ ├── Chapter02.scala │ ├── Chapter03.scala │ ├── Chapter04.scala │ ├── Chapter05.scala │ ├── Chapter06.scala │ ├── Chapter07.scala │ ├── Chapter0701a.scala │ ├── Chapter0701b.scala │ ├── Chapter08.scala │ ├── Chapter09.scala │ ├── Chapter10.scala │ ├── Chapter11.scala │ ├── Chapter12.scala │ ├── Chapter13.scala │ ├── Chapter14.scala │ ├── Chapter15.scala │ ├── Chapter16.scala │ ├── Chapter17.scala │ ├── Chapter18.scala │ ├── Chapter19.scala │ ├── Chapter20.scala │ ├── Chapter21.scala │ └── Utils.scala └── test └── scala ├── Chapter01Spec.scala ├── Chapter02Spec.scala ├── Chapter03Spec.scala ├── Chapter04Spec.scala ├── Chapter05Spec.scala ├── Chapter06Spec.scala ├── Chapter07Spec.scala ├── Chapter08Spec.scala ├── Chapter09Spec.scala ├── Chapter10Spec.scala ├── Chapter11Spec.scala ├── Chapter12Spec.scala ├── Chapter13Spec.scala ├── Chapter14Spec.scala ├── Chapter15Spec.scala ├── Chapter16Spec.scala ├── Chapter17Spec.scala ├── Chapter18Spec.scala ├── Chapter19Spec.scala ├── Chapter20Spec.scala ├── Chapter21Spec.scala └── TestUtils.scala /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | pom.xml.tag 3 | pom.xml.releaseBackup 4 | pom.xml.versionsBackup 5 | pom.xml.next 6 | release.properties 7 | 8 | *.iml 9 | .idea 10 | .project 11 | .classpath 12 | .DS_Store 13 | *.log 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Scala for the Impatient 2 | My solutions to the exercises from the excellent [Scala for the Impatient](http://horstmann.com/scala/) book. 3 | 4 | 1. [The Basics](src/main/scala/Chapter01.scala) => [tests](src/test/scala/Chapter01Spec.scala) 5 | 2. [Control Structures and Functions](src/main/scala/Chapter02.scala) => [tests](src/test/scala/Chapter02Spec.scala) 6 | 3. [Working with Arrays](src/main/scala/Chapter03.scala) => [tests](src/test/scala/Chapter03Spec.scala) 7 | 4. [Maps and Tuples](src/main/scala/Chapter04.scala) => [tests](src/test/scala/Chapter04Spec.scala) 8 | 5. [Classes](src/main/scala/Chapter05.scala) ([task 5 java](src/main/java/Chapter05Car.java)) => [tests](src/test/scala/Chapter05Spec.scala) 9 | 6. [Objects](src/main/scala/Chapter06.scala) => [tests](src/test/scala/Chapter06Spec.scala) 10 | 7. [Packages and Imports](src/main/scala/Chapter07.scala) ([task 1a](src/main/scala/Chapter0701a.scala), [1b](src/main/scala/Chapter0701b.scala)) => [tests](src/test/scala/Chapter07Spec.scala) 11 | 8. [Inheritance](src/main/scala/Chapter08.scala) => [tests](src/test/scala/Chapter08Spec.scala) 12 | 9. [Files and Regular Expressions](src/main/scala/Chapter09.scala) => [tests](src/test/scala/Chapter09Spec.scala) 13 | 10. [Traits](src/main/scala/Chapter10.scala) => [tests](src/test/scala/Chapter10Spec.scala) 14 | 11. [Operators](src/main/scala/Chapter11.scala) => [tests](src/test/scala/Chapter11Spec.scala) 15 | 12. [Higher-order functions](src/main/scala/Chapter12.scala) => [tests](src/test/scala/Chapter12Spec.scala) 16 | 13. [Collections](src/main/scala/Chapter13.scala) => [tests](src/test/scala/Chapter13Spec.scala) 17 | 14. [Pattern matching and case classes](src/main/scala/Chapter14.scala) => [tests](src/test/scala/Chapter14Spec.scala) 18 | 15. [Annotations](src/main/scala/Chapter15.scala) ([task 4 java](src/main/java/Chapter15Task4.java), [5 java](src/main/java/Chapter15Task5.java)) => [tests](src/test/scala/Chapter15Spec.scala) 19 | 16. [XML Processing](src/main/scala/Chapter16.scala) => [tests](src/test/scala/Chapter16Spec.scala) 20 | 17. [Type parameters](src/main/scala/Chapter17.scala) => [tests](src/test/scala/Chapter17Spec.scala) 21 | 18. [Advanced types](src/main/scala/Chapter18.scala) => [tests](src/test/scala/Chapter18Spec.scala) 22 | 19. [Parsing](src/main/scala/Chapter19.scala) => [tests](src/test/scala/Chapter19Spec.scala) 23 | 20. [Actors](src/main/scala/Chapter20.scala) => [tests](src/test/scala/Chapter20Spec.scala) 24 | 21. [Implicits](src/main/scala/Chapter21.scala) => [tests](src/test/scala/Chapter21Spec.scala) 25 | 22. Delimited continuations 26 | 27 | Almost all solutions are covered with corresponding tests. 28 | You can build and run tests and see the coverage by using the following command: 29 | 30 | ```bash 31 | activator clean coverage test coverageReport 32 | ``` 33 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | 2 | name := "scala-impatient" 3 | 4 | organization := "com.github.viktor-podzigun" 5 | 6 | version := "1.0-SNAPSHOT" 7 | 8 | scalaVersion := "2.11.7" 9 | 10 | //fork := false 11 | 12 | // to run coverage tests use: 13 | // 14 | // activator clean coverage test coverageReport 15 | // 16 | // 17 | //coverageEnabled := true 18 | 19 | coverageMinimum := 80 20 | 21 | coverageFailOnMinimum := true 22 | 23 | scalacOptions ++= Seq("-unchecked", "-deprecation", "-feature") 24 | 25 | libraryDependencies ++= Seq( 26 | "org.scala-lang.modules" %% "scala-xml" % "1.0.2", 27 | "org.scala-lang.modules" %% "scala-parser-combinators" % "1.0.4", 28 | "org.scala-lang" % "scala-actors" % "2.11.5", 29 | "junit" % "junit" % "4.11", 30 | "org.hamcrest" % "hamcrest-all" % "1.3", 31 | "org.scala-lang" % "scala-reflect" % "2.11.7" % "test", 32 | "org.scalatest" %% "scalatest" % "2.2.4" % "test" 33 | ) 34 | 35 | resolvers ++= Seq( 36 | // "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots", 37 | // "Sonatype OSS Releases" at "https://oss.sonatype.org/content/repositories/releases", 38 | "Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/" 39 | ) 40 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.5 -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | 2 | addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.3.5") 3 | -------------------------------------------------------------------------------- /src/main/java/Chapter02Task4.java: -------------------------------------------------------------------------------- 1 | 2 | public class Chapter02Task4 { 3 | 4 | public static void javaForLoop() { 5 | for (int i = 10; i >= 0; i--) { 6 | // use scala output to be able to redirect the output and test the results 7 | scala.Console.println(i); 8 | //System.out.println(i); 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/Chapter05Car.java: -------------------------------------------------------------------------------- 1 | 2 | public class Chapter05Car { 3 | 4 | private final String manufacturer; 5 | private final String modelName; 6 | private final int modelYear; 7 | private final String licensePlate; 8 | 9 | public Chapter05Car(final String manufacturer, final String modelName) { 10 | this(manufacturer, modelName, -1); 11 | } 12 | 13 | public Chapter05Car(final String manufacturer, final String modelName, final int modelYear) { 14 | this(manufacturer, modelName, modelYear, ""); 15 | } 16 | 17 | public Chapter05Car(final String manufacturer, 18 | final String modelName, 19 | final String licensePlate) { 20 | 21 | this(manufacturer, modelName, -1, licensePlate); 22 | } 23 | 24 | public Chapter05Car(final String manufacturer, 25 | final String modelName, 26 | final int modelYear, 27 | final String licensePlate) { 28 | 29 | this.manufacturer = manufacturer; 30 | this.modelName = modelName; 31 | this.modelYear = modelYear; 32 | this.licensePlate = licensePlate; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/Chapter15Task4.java: -------------------------------------------------------------------------------- 1 | 2 | public class Chapter15Task4 { 3 | 4 | public static int scalaSum(final int... args) { 5 | return Chapter15.sum(args); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/Chapter15Task5.java: -------------------------------------------------------------------------------- 1 | import java.io.IOException; 2 | 3 | public class Chapter15Task5 { 4 | 5 | public static String scalaFileToString(final String file) { 6 | try { 7 | return Chapter15.fileToString(file); 8 | } catch (final IOException e) { 9 | throw new RuntimeException(e); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/resources/Chapter16Task04.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 |
6 |
7 |
14 |
15 |
16 |
17 |
19 | Ref 2 20 |
21 |
27 |
28 |
Got from here:
91 | *
http://javarevisited.blogspot.de/2015/03/how-to-reverse-array-in-place-in-java.html
92 | */
93 | private def reverse[T](a: Array[T]): Array[T] = {
94 | for (i <- 0 until a.length / 2) {
95 | // swap elements
96 | val temp = a(i)
97 | val j = a.length - 1 - i
98 | a(i) = a(j)
99 | a(j) = temp
100 | }
101 |
102 | a
103 | }
104 |
105 | /**
106 | * Task 6a:
107 | * How do you rearrange the elements of an Array[Int]
108 | * so that they appear in reverse sorted order?
109 | */
110 | def reverseSortArray(a: Array[Int]): Array[Int] = {
111 | Sorting.quickSort(a)
112 | reverse(a)
113 | }
114 |
115 | /**
116 | * Task 6b:
117 | * How do you rearrange the elements of an ArrayBuffer[Int]
118 | * so that they appear in reverse sorted order?
119 | */
120 | def reverseSortArrayBuffer(buf: ArrayBuffer[Int]): ArrayBuffer[Int] = {
121 | val arr: Array[Int] = reverseSortArray(buf.toArray)
122 | buf.clear()
123 | arr.copyToBuffer(buf)
124 | buf
125 | }
126 |
127 | /**
128 | * Task 7:
129 | * Write a code snippet that produces all values from an array with duplicates removed.
130 | * (Hint: Look at Scaladoc.)
131 | */
132 | def removeDuplicates(a: Array[Int]): Array[Int] = a.distinct
133 |
134 | /**
135 | * Task 8:
136 | * Rewrite the example at the end of Section 3.4, "Transforming Arrays", on page 34
137 | * using the drop method for dropping the index of the first match.
138 | * Look the method up in Scaladoc.
139 | */
140 | def dropNegativesExceptFirst(a: ArrayBuffer[Int]): ArrayBuffer[Int] = {
141 | val allNegativeIndexes = for (i <- a.indices if a(i) < 0) yield i
142 | val indexes = allNegativeIndexes.drop(1)
143 |
144 | for (j <- indexes.indices.reverse) a.remove(indexes(j))
145 | a
146 | }
147 |
148 | /**
149 | * Task 9:
150 | * Make a collection of all time zones returned by java.util.TimeZone.getAvailableIDs
151 | * that are in America. Strip off the "America/" prefix and sort the result.
152 | */
153 | def americaTimeZones: Array[String] = {
154 | TimeZone.getAvailableIDs.filter(_.startsWith("America/"))
155 | .map(_.stripPrefix("America/"))
156 | .sorted
157 | }
158 |
159 | /**
160 | * Task 10:
161 | * Import java.awt.datatransfer._ and make an object of type SystemFlavorMap with the call
162 | *
163 | * val flavors = SystemFlavorMap.getDefaultFlavorMap().asInstanceOf[SystemFlavorMap]
164 | *
165 | * Then call the getNativesForFlavor method with parameter DataFlavor.imageFlavor
166 | * and get the return value as a Scala buffer. (Why this obscure class? It’s hard
167 | * to find uses of java.util.List in the standard Java library.)
168 | */
169 | def javaListAsScalaBuffer: mutable.Buffer[String] = {
170 | import scala.collection.JavaConversions.asScalaBuffer
171 |
172 | SystemFlavorMap.getDefaultFlavorMap.asInstanceOf[SystemFlavorMap]
173 | .getNativesForFlavor(DataFlavor.imageFlavor)
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/src/main/scala/Chapter04.scala:
--------------------------------------------------------------------------------
1 | import java.util
2 | import java.util.Scanner
3 |
4 | import scala.collection.JavaConversions.{mapAsScalaMap, propertiesAsScalaMap}
5 | import scala.collection.immutable.SortedMap
6 | import scala.collection.mutable
7 | import scala.collection.mutable.ListBuffer
8 |
9 | object Chapter04 {
10 |
11 | /**
12 | * Task 1:
13 | * Set up a map of prices for a number of gizmos that you covet.
14 | * Then produce a second map with the same keys and the prices at a 10 percent discount.
15 | */
16 | def gizmosWithReducedPrice(): Map[String, Int] = {
17 | val gizmos = Map("iPhone" -> 600,
18 | "iPad" -> 500,
19 | "MacBook Pro" -> 2000,
20 | "ScalaDays 2016 Berlin" -> 750)
21 |
22 | gizmos.mapValues(price => price - (price / 10))
23 | }
24 |
25 | /**
26 | * Task 2:
27 | * Write a program that reads words from a file. Use a mutable map to count
28 | * how often each word appears. To read the words, simply use a java.util.Scanner:
29 | *
30 | * val in = new java.util.Scanner(new java.io.File("myfile.txt"))
31 | * while (in.hasNext()) process in.next()
32 | *
33 | */
34 | def countWordsMutableMap(): mutable.Map[String, Int] = {
35 | val words = new mutable.HashMap[String, Int]
36 |
37 | processWords(w => words(w) = words.getOrElse(w, 0) + 1)
38 | words
39 | }
40 |
41 | private def processWords(process: String => Unit): Unit = {
42 | val in = new Scanner(getClass.getResourceAsStream("/myfile.txt"))
43 | try {
44 | while (in.hasNext) {
45 | process(in.next())
46 | }
47 | } finally {
48 | in.close()
49 | }
50 | }
51 |
52 | /**
53 | * Task 3:
54 | * Repeat the preceding exercise with an immutable map.
55 | */
56 | def countWordsImmutableMap(): Map[String, Int] = {
57 | var words = Map[String, Int]()
58 |
59 | processWords(w => words += w -> (words.getOrElse(w, 0) + 1))
60 | words
61 | }
62 |
63 | /**
64 | * Task 4:
65 | * Repeat the preceding exercise with a sorted map,
66 | * so that the words are printed in sorted order.
67 | */
68 | def countWordsSortedMap(): Map[String, Int] = {
69 | var words = SortedMap[String, Int]()
70 |
71 | processWords(w => words += w -> (words.getOrElse(w, 0) + 1))
72 | words
73 | }
74 |
75 | /**
76 | * Task 5:
77 | * Repeat the preceding exercise with a java.util.TreeMap
78 | * that you adapt to the Scala API.
79 | */
80 | def countWordsTreeMap(): mutable.Map[String, Int] = {
81 | val words = new util.TreeMap[String, Int]()
82 |
83 | processWords(w => words(w) = words.getOrElse(w, 0) + 1)
84 | words
85 | }
86 |
87 | /**
88 | * Task 6:
89 | * Define a linked hash map that maps "Monday" to java.util.Calendar.MONDAY,
90 | * and similarly for the other weekdays. Demonstrate that the elements
91 | * are visited in insertion order.
92 | */
93 | def weekdaysLinkedHashMap(): mutable.Map[String, Int] = {
94 | val weekdays = mutable.LinkedHashMap[String, Int]("Monday" -> util.Calendar.MONDAY)
95 | weekdays += ("Tuesday" -> util.Calendar.TUESDAY,
96 | "Wednesday" -> util.Calendar.WEDNESDAY,
97 | "Thursday" -> util.Calendar.THURSDAY,
98 | "Friday" -> util.Calendar.FRIDAY,
99 | "Saturday" -> util.Calendar.SATURDAY,
100 | "Sunday" -> util.Calendar.SUNDAY)
101 |
102 | weekdays
103 | }
104 |
105 | /**
106 | * Task 7:
107 | * Print a table of all Java properties, like this:
108 | *
109 | * java.runtime.name | Java(TM) SE Runtime Environment
110 | * sun.boot.library.path | /home/apps/jdk1.6.0_21/jre/lib/i386
111 | * java.vm.version | 17.0-b16
112 | * java.vm.vendor | Sun Microsystems Inc.
113 | * java.vendor.url | http://java.sun.com/
114 | * path.separator | :
115 | * java.vm.name | Java HotSpot(TM) Server VM
116 | *
117 | * You need to find the length of the longest key before you can print the table.
118 | */
119 | def formatJavaProperties(): List[String] = {
120 | val props: collection.Map[String, String] = System.getProperties
121 | val maxKeyLen = props.foldLeft(0)((maxLen, entry) => {
122 | if (maxLen < entry._1.length) entry._1.length
123 | else maxLen
124 | })
125 |
126 | val result = ListBuffer[String]()
127 | for ((key, value) <- props) {
128 | result += key.padTo(maxKeyLen, ' ') + " | " + value
129 | }
130 |
131 | result.toList
132 | }
133 |
134 | /**
135 | * Task 8:
136 | * Write a function minmax(values: Array[Int]) that returns a pair containing
137 | * the smallest and largest values in the array.
138 | */
139 | def minmax(values: Array[Int]): (Int, Int) = (values.min, values.max)
140 |
141 | /**
142 | * Task 9:
143 | * Write a function lteqgt(values: Array[Int], v: Int) that returns a triple containing
144 | * the counts of values less than v , equal to v , and greater than v.
145 | */
146 | def lteqgt(values: Array[Int], v: Int): (Int, Int, Int) = {
147 | // val count = values.count _
148 | //
149 | // (count(_ < v), count(_ == v), count(_ > v))
150 | var lt, eq, gt = 0
151 |
152 | for (e <- values) {
153 | if (e < v) lt += 1
154 | else if (e == v) eq += 1
155 | else gt += 1
156 | }
157 |
158 | (lt, eq, gt)
159 | }
160 |
161 | def main(args: Array[String]) {
162 | // task 2
163 | println(countWordsMutableMap().mkString("\n"))
164 |
165 | // task 7
166 | println(formatJavaProperties().mkString("\n"))
167 |
168 | // task 10
169 | println("Hello".zip("World").mkString("\n"))
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/src/main/scala/Chapter05.scala:
--------------------------------------------------------------------------------
1 | import scala.beans.BeanProperty
2 |
3 | object Chapter05 {
4 |
5 | /**
6 | * Task 1:
7 | * Improve the Counter class in Section 5.1, "Simple Classes and Parameterless Methods"
8 | * on page 51 so that it doesn't turn negative at Int.MaxValue
9 | */
10 | class Counter01(private var value: Int = 0) {
11 |
12 | def increment(): Int = {
13 | if (value == Int.MaxValue) {
14 | throw new IllegalStateException("counter reached Int.MaxValue")
15 | }
16 |
17 | value += 1
18 | value
19 | }
20 |
21 | def current: Int = value
22 | }
23 |
24 | /**
25 | * Task 2:
26 | * Write a class BankAccount with methods deposit and withdraw,
27 | * and a read-only property balance.
28 | */
29 | class BankAccount02 {
30 |
31 | private var amount: Int = 0
32 |
33 | def deposit(sum: Int): Unit = {
34 | amount += sum
35 | }
36 |
37 | def withdraw(sum: Int): Unit = {
38 | amount -= sum
39 | }
40 |
41 | def balance: Int = amount
42 | }
43 |
44 | /**
45 | * Task 3:
46 | * Write a class Time with read-only properties hours and minutes and a method
47 | *
48 | * before(other: Time): Boolean
49 | *
50 | * that checks whether this time comes before the other.
51 | *
52 | * A Time object should be constructed as new Time(hrs, min), where hrs is in
53 | * military time format (between 0 and 23).
54 | */
55 | class Time03(private val hours: Int, private val minutes: Int) {
56 |
57 | def before(other: Time03): Boolean = {
58 | if (hours < other.hours) true
59 | else if (hours == other.hours && minutes < other.minutes) true
60 | else false
61 | }
62 | }
63 |
64 | /**
65 | * Task 4:
66 | * Reimplement the Time class from the preceding exercise so that the internal
67 | * representation is the number of minutes since midnight (between 0 and 24 × 60 – 1).
68 | * Do not change the public interface. That is, client code should be unaffected by your change.
69 | */
70 | class Time04(hrs: Int, min: Int) {
71 |
72 | private val minutes: Int = (hrs * 60) + min
73 |
74 | def before(other: Time04): Boolean = {
75 | minutes < other.minutes
76 | }
77 | }
78 |
79 | /**
80 | * Task 5:
81 | * Make a class Student with read-write JavaBeans properties name (of type String )
82 | * and id (of type Long ). What methods are generated? (Use javap to check.)
83 | * Can you call the JavaBeans getters and setters in Scala? Should you?
84 | */
85 | class Student05 {
86 |
87 | @BeanProperty
88 | var name: String = ""
89 |
90 | @BeanProperty
91 | var id: Long = 0
92 | }
93 |
94 | /**
95 | * Task 6:
96 | * In the Person class of Section 5.1, "Simple Classes and Parameterless Methods,"
97 | * on page 51, provide a primary constructor that turns negative ages to 0.
98 | */
99 | class Person06(inAge: Int) {
100 |
101 | var age: Int = if (inAge < 0) 0 else inAge
102 | }
103 |
104 | /**
105 | * Task 7:
106 | * Write a class Person with a primary constructor that accepts a string containing
107 | * a first name, a space, and a last name, such as new Person("Fred Smith").
108 | * Supply read-only properties firstName and lastName.
109 | * Should the primary constructor parameter be a var, a val, or a plain parameter? Why?
110 | */
111 | class Person07 private(names: Array[String]) {
112 |
113 | val firstName: String = names(0)
114 | val lastName: String = names(1)
115 |
116 | def this(name: String) {
117 | this(Person07.parseName(name))
118 | }
119 | }
120 |
121 | private object Person07 {
122 | def parseName(name: String): Array[String] = {
123 | val names = name.split(" ")
124 | if (names.length < 2) {
125 | throw new IllegalArgumentException("name should contain a first name, a space" +
126 | ", and a last name, such as \"Fred Smith\"")
127 | }
128 |
129 | names
130 | }
131 | }
132 |
133 | /**
134 | * Task 8:
135 | * Make a class Car with read-only properties for manufacturer, model name,
136 | * and model year, and a read-write property for the license plate.
137 | * Supply four constructors. All require the manufacturer and model name.
138 | * Optionally, model year and license plate can also be specified in the constructor.
139 | * If not, the model year is set to -1 and the license plate to the empty string.
140 | * Which constructor are you choosing as the primary constructor? Why?
141 | */
142 | class Car08(val manufacturer: String,
143 | val modelName: String,
144 | val modelYear: Int = -1,
145 | val licensePlate: String = "") {
146 |
147 | def this(manufacturer: String, modelName: String, licensePlate: String) {
148 | this(manufacturer, modelName, -1, licensePlate)
149 | }
150 |
151 | // Can be removed
152 | // see http://stackoverflow.com/questions/24480989/scala-auxilliary-constructor-behavior
153 | //
154 | // def this(manufacturer: String, modelName: String, modelYear: Int) {
155 | // this(manufacturer, modelName, modelYear, "")
156 | // }
157 | //
158 | // def this(manufacturer: String, modelName: String) {
159 | // this(manufacturer, modelName, -1)
160 | // }
161 | }
162 |
163 | /**
164 | * Task 9:
165 | * Reimplement the class of the preceding exercise in Java, C#, or C++ (your choice).
166 | * How much shorter is the Scala class?
167 | */
168 | // see src/main/java/Chapter05Car.java
169 |
170 | /**
171 | * Task 10:
172 | * Consider the class
173 | *
174 | * class Employee(val name: String, var salary: Double) {
175 | * def this() { this("John Q. Public", 0.0) }
176 | * }
177 | *
178 | * Rewrite it to use explicit fields and a default primary constructor.
179 | * Which form do you prefer? Why?
180 | */
181 | class Employee10 {
182 | val name: String = "John Q. Public"
183 | var salary: Double = 0.0
184 | }
185 | }
186 |
--------------------------------------------------------------------------------
/src/main/scala/Chapter06.scala:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Task 1:
4 | * Write an object Conversions with methods inchesToCentimeters, gallonsToLiters,
5 | * and milesToKilometers.
6 | */
7 | object Conversions {
8 |
9 | def inchesToCentimeters(inches: Double): Double = inches * 2.54
10 |
11 | def gallonsToLiters(gallons: Double): Double = gallons * 3.785
12 |
13 | def milesToKilometers(miles: Double): Double = miles / 0.62137
14 | }
15 |
16 | /**
17 | * Task 2:
18 | * The preceding problem wasn't very object-oriented. Provide a general super-class
19 | * UnitConversion and define objects InchesToCentimeters, GallonsToLiters, and
20 | * MilesToKilometers that extend it.
21 | */
22 | abstract class UnitConversion {
23 |
24 | def convert(inches: Double): Double
25 | }
26 |
27 | object InchesToCentimeters extends UnitConversion {
28 |
29 | override def convert(inches: Double) = inches * 2.54
30 | }
31 |
32 | object GallonsToLiters extends UnitConversion {
33 |
34 | override def convert(gallons: Double) = gallons * 3.785
35 | }
36 |
37 | object MilesToKilometers extends UnitConversion {
38 |
39 | override def convert(miles: Double) = miles / 0.62137
40 | }
41 |
42 | /**
43 | * Task 3:
44 | * Define an Origin object that extends java.awt.Point. Why is this not actually a good idea?
45 | * (Have a close look at the methods of the Point class.)
46 | */
47 | object Origin extends java.awt.Point {
48 | // Its not a good idea since java.awt.Point class is mutable.
49 | }
50 |
51 | package task0604 {
52 |
53 | /**
54 | * Task 4:
55 | *
56 | * Define a Point class with a companion object so that you can construct Point
57 | * instances as Point(3, 4), without using new.
58 | */
59 | class Point private(val x: Int, val y: Int) {
60 | }
61 |
62 | object Point {
63 | def apply(x: Int, y: Int) = new Point(x, y)
64 | }
65 |
66 | }
67 |
68 | /**
69 | * Task 5:
70 | * Write a Scala application, using the App trait, that prints the command-line
71 | * arguments in reverse order, separated by spaces. For example:
72 | *
73 | * scala Reverse Hello World
74 | *
75 | * should print
76 | *
77 | * World Hello
78 | *
79 | */
80 | object Reverse extends App {
81 | println(args.reverse.mkString(" "))
82 | }
83 |
84 | /**
85 | * Task 6:
86 | * Write an enumeration describing the four playing card suits so that the toString method
87 | * returns ♣, ♦, ♥, or ♠.
88 | */
89 | object PlayingCard extends Enumeration {
90 |
91 | val Clubs = Value("♣")
92 | val Diamonds = Value("♦")
93 | val Hearts = Value("♥")
94 | val Spades = Value("♠")
95 |
96 | /**
97 | * Task 7:
98 | * Implement a function that checks whether a card suit value from the preceding exercise
99 | * is red.
100 | */
101 | def isRed(card: PlayingCard.Value): Boolean = {
102 | card == Hearts || card == Diamonds
103 | }
104 | }
105 |
106 | /**
107 | * Task 8:
108 | * Write an enumeration describing the eight corners of the RGB color cube.
109 | * As IDs, use the color values (for example, 0xff0000 for Red).
110 | */
111 | object RGB extends Enumeration {
112 |
113 | val Black = Value(0x000000)
114 | val White = Value(0xffffff)
115 | val Red = Value(0xff0000)
116 | val Lime = Value(0x00ff00)
117 | val Blue = Value(0x0000ff)
118 | val Yellow = Value(0xffff00)
119 | val Cyan = Value(0x00ffff)
120 | val Magenta = Value(0xff00ff)
121 | }
122 |
--------------------------------------------------------------------------------
/src/main/scala/Chapter07.scala:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Task 1:
4 | *
5 | * Write an example program to demonstrate that 6 | *
7 | * package com.horstmann.impatient
8 | *
9 | * is not the same as
10 | *
11 | * package com
12 | * package horstmann
13 | * package impatient
14 | *
15 | *
16 | * @see Chapter0701a.scala for solution part A
17 | * @see Chapter0701b.scala for solution part B
18 | */
19 | package com {
20 |
21 | object FromCom {
22 | val value = 1
23 | }
24 |
25 | package horstmann {
26 |
27 | object FromHorstmann {
28 | val value = 2
29 | }
30 |
31 | package impatient {
32 |
33 | object FromImpatient {
34 | val value = 3
35 | }
36 | }
37 | }
38 | }
39 |
40 | /**
41 | * Task 2:
42 | *
43 | * Write a puzzler that baffles your Scala friends, using a package com
44 | * that isn’t at the top level.
45 | */
46 | package puzzler {
47 |
48 | package com {
49 | object FromCom {
50 | val value = 21
51 | }
52 | }
53 | }
54 |
55 | /**
56 | * Task 3:
57 | *
58 | *
Write a package random with functions 59 | * nextInt(): Int, 60 | * nextDouble(): Double, 61 | * and setSeed(seed: Int): Unit. 62 | * 63 | *
To generate random numbers, use the linear congruential generator 64 | * next = previous × a + b mod 2n, 65 | * where a = 1664525, b = 1013904223, and n = 32. 66 | */ 67 | package object random { 68 | 69 | private val addition: Int = (1013904223 % (1L << 32)).toInt 70 | private var seed : Int = 0 71 | 72 | def nextInt(): Int = { 73 | seed = (seed * 1664525) + addition 74 | 75 | if (seed < 0) ~seed 76 | else seed 77 | } 78 | 79 | def nextDouble(): Double = { 80 | nextInt() / (Int.MaxValue + 1.0) 81 | } 82 | 83 | def setSeed(seed: Int): Unit = this.seed = seed 84 | } 85 | 86 | /** 87 | * Task 4: 88 | * 89 | *
Why do you think the Scala language designers provided the package object syntax instead 90 | * of simply letting you add functions and variables to a package? 91 | * 92 | *
Solution:
93 | * They decided to make it explicit by adding just one word "object" to package declaration,
94 | * in my opinion, for a couple of reasons:
95 | *
What is the meaning of private[com] def giveRaise(rate: Double)
?
107 | * Is it useful?
108 | */
109 | package com {
110 |
111 | /**
112 | * private[com]
makes definition package-private, meaning it is visible within
113 | * the same package and all sub-packages.
114 | */
115 | object VisibilityDef {
116 | private[com] def giveRaise(rate: Double): Double = rate * 0.5
117 | }
118 |
119 | object VisibilityUsage {
120 | println(VisibilityDef.giveRaise(1))
121 | }
122 |
123 | package horstmann {
124 |
125 | object VisibilityUsage {
126 | println(VisibilityDef.giveRaise(1))
127 | }
128 | }
129 | }
130 |
131 | /**
132 | * Task 6:
133 | *
134 | *
Write a program that copies all elements from a Java hash map into a Scala hash map. 135 | * Use imports to rename both classes. 136 | */ 137 | object Chapter0706 { 138 | 139 | /** 140 | * Task 7: 141 | * 142 | *
In the preceding exercise, move all imports into the innermost scope possible. 143 | */ 144 | import java.util.{HashMap => JavaHashMap} 145 | 146 | import scala.collection.mutable.{HashMap => ScalaHashMap} 147 | 148 | def fromJavaHashMap(javaHashMap: JavaHashMap[String, Int]): ScalaHashMap[String, Int] = { 149 | import scala.collection.JavaConversions.iterableAsScalaIterable 150 | 151 | val result = new ScalaHashMap[String, Int] 152 | for (entry <- javaHashMap.entrySet()) { 153 | result(entry.getKey) = entry.getValue 154 | } 155 | 156 | result 157 | } 158 | } 159 | 160 | /** 161 | * Task 8: 162 | * 163 | *
What is the effect of 164 | *
165 | * import java._
166 | * import javax._
167 | *
168 | * Is this a good idea?
169 | */
170 | object Chapter0708 {
171 |
172 | import java._
173 |
174 | /**
175 | * Since we imported everything from java package, we can use sub-packages.
176 | */
177 | def doSomething(list: util.List[String]) {
178 | }
179 | }
180 |
181 | /**
182 | * Task 9:
183 | *
184 | * Write a program that imports the java.lang.System
class,
185 | * reads the user name from the user.name
system property,
186 | * reads a password from the Console
object, and prints a message
187 | * to the standard error stream if the password is not "secret".
188 | * Otherwise, print a greeting to the standard output stream.
189 | * Do not use any other imports, and do not use any qualified names (with dots).
190 | */
191 | object Chapter0709 extends App {
192 | //import java.lang.System
193 |
194 | val userName = System.getProperty("user.name")
195 | val password: String = readLine("Please, enter password: ")
196 |
197 | if (password != "secret") error("Wrong password!")
198 | else println("Welcome " + userName)
199 | }
200 |
201 | /**
202 | * Task 10:
203 | *
204 | *
Apart from StringBuilder, what other members of java.lang does the scala package override? 205 | */ 206 | object JavaLangOverrides extends App { 207 | 208 | val overrides = List[Class[_]](classOf[Boolean], 209 | classOf[Byte], 210 | classOf[Double], 211 | classOf[Float], 212 | classOf[Iterable[_]], 213 | classOf[Long], 214 | classOf[Short], 215 | classOf[StringBuilder]) 216 | 217 | println(overrides.mkString("\n")) 218 | } 219 | -------------------------------------------------------------------------------- /src/main/scala/Chapter0701a.scala: -------------------------------------------------------------------------------- 1 | package com.horstmann.impatient 2 | 3 | /** 4 | * Solution A for Task 1: 5 | * 6 | *
FromCom
and FromHorstmann
are not accessible from here.
7 | * They require additional imports:
8 | *
9 | * import com.FromCom
10 | * import com.horstmann.FromHorstmann
11 | *
12 | */
13 | object Chapter0701a {
14 |
15 | //println(FromCom.value)
16 | //println(FromHorstmann.value)
17 | println(FromImpatient.value)
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/scala/Chapter0701b.scala:
--------------------------------------------------------------------------------
1 | package com
2 | package horstmann
3 | package impatient
4 |
5 | /**
6 | * Solution B for Task 1:
7 | *
8 | *
130 | |
131 | |
168 | |
169 | |
173 | | Ref 2
174 | |
181 | | FromCom
and FromHorstmann
are accessible from here.
9 | * No additional imports are required.
10 | */
11 | object Chapter0701b {
12 |
13 | println(FromCom.value)
14 | println(FromHorstmann.value)
15 | println(FromImpatient.value)
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/scala/Chapter08.scala:
--------------------------------------------------------------------------------
1 | import scala.collection.mutable.ListBuffer
2 |
3 | /**
4 | * Task 1:
5 | *
6 | * Extend the following `BankAccount` class to a `CheckingAccount` class that charges $1
7 | * for every deposit and withdrawal.
8 | * {{{
9 | * class BankAccount(initialBalance: Double) {
10 | * private var balance = initialBalance
11 | * def deposit(amount: Double) = { balance += amount; balance }
12 | * def withdraw(amount: Double) = { balance -= amount; balance }
13 | * }
14 | * }}}
15 | */
16 | class BankAccount(initialBalance: Double) {
17 | private var balance = initialBalance
18 |
19 | def deposit(amount: Double) = {
20 | balance += amount
21 | balance
22 | }
23 |
24 | def withdraw(amount: Double) = {
25 | balance -= amount
26 | balance
27 | }
28 | }
29 |
30 | class CheckingAccount(initialBalance: Double) extends BankAccount(initialBalance) {
31 |
32 | private val charge: Double = 1
33 |
34 | override def deposit(amount: Double) = super.deposit(amount - charge)
35 |
36 | override def withdraw(amount: Double) = super.withdraw(amount + charge)
37 | }
38 |
39 | /**
40 | * Task 2:
41 | *
42 | * Extend the `BankAccount` class of the preceding exercise into a class `SavingsAccount`
43 | * that earns interest every month (when a method `earnMonthlyInterest` is called)
44 | * and has three free deposits or withdrawals every month. Reset the transaction
45 | * count in the `earnMonthlyInterest` method.
46 | */
47 | class SavingsAccount(initialBalance: Double) extends BankAccount(initialBalance) {
48 |
49 | private val monthlyInterest: Double = 0.01
50 | private val maxFreeTransactions: Int = 3
51 |
52 | private var transactionsCount: Int = 0
53 | private var balance: Double = initialBalance
54 |
55 | override def deposit(amount: Double) = {
56 | balance = super.deposit(amount - charge)
57 | balance
58 | }
59 |
60 | override def withdraw(amount: Double) = {
61 | balance = super.withdraw(amount + charge)
62 | balance
63 | }
64 |
65 | def getBalance: Double = {
66 | balance
67 | }
68 |
69 | def earnMonthlyInterest(): Unit = {
70 | transactionsCount = 0
71 |
72 | balance = super.deposit(balance * monthlyInterest)
73 | }
74 |
75 | private def charge: Double = {
76 | if (transactionsCount < maxFreeTransactions) {
77 | transactionsCount += 1
78 | return 0.0
79 | }
80 |
81 | 1.0
82 | }
83 | }
84 |
85 | package task0803 {
86 |
87 | /**
88 | * Task 3:
89 | *
90 | * Consult your favorite Java or C++ textbook that is sure to have an example
91 | * of a toy inheritance hierarchy, perhaps involving employees, pets, graphical
92 | * shapes, or the like. Implement the example in Scala.
93 | */
94 | abstract class Shape {
95 | def draw(): Unit
96 |
97 | def erase(): Unit
98 | }
99 |
100 | class Circle extends Shape {
101 |
102 | override def draw(): Unit = {
103 | println("drawing Circle...")
104 | }
105 |
106 | override def erase(): Unit = {
107 | println("erasing Circle...")
108 | }
109 | }
110 |
111 | class Square extends Shape {
112 |
113 | override def draw(): Unit = {
114 | println("drawing Square...")
115 | }
116 |
117 | override def erase(): Unit = {
118 | println("erasing Square...")
119 | }
120 | }
121 |
122 | object Shapes extends App {
123 |
124 | cleanAndPaint(new Circle)
125 | cleanAndPaint(new Square)
126 |
127 | def cleanAndPaint(shape: Shape): Unit = {
128 | shape.erase()
129 | shape.draw()
130 | }
131 | }
132 |
133 | }
134 |
135 | package task0804 {
136 |
137 | /**
138 | * Task 4:
139 | *
140 | * Define an abstract class `Item` with methods `price` and `description`. A `SimpleItem`
141 | * is an item whose `price` and `description` are specified in the constructor. Take advantage
142 | * of the fact that a val can override a def. A `Bundle` is an item that contains other items.
143 | * Its price is the sum of the prices in the bundle. Also provide a mechanism for adding items
144 | * to the bundle and a suitable description method.
145 | */
146 | abstract class Item {
147 |
148 | /** Price in minor units */
149 | def price: Int
150 |
151 | def description: String
152 | }
153 |
154 | class SimpleItem(override val price: Int, override val description: String) extends Item
155 |
156 | class Bundle extends Item {
157 |
158 | private val items = new ListBuffer[Item]
159 |
160 | def addItem(item: Item): Bundle = {
161 | items += item
162 | this
163 | }
164 |
165 | override def price = items.foldLeft(0)((sum, item) => sum + item.price)
166 |
167 | override def description = items.map(_.description).mkString("\n\n")
168 | }
169 |
170 | }
171 |
172 | package task0805 {
173 |
174 | /**
175 | * Task 5:
176 | *
177 | * Design a class `Point` whose x and y coordinate values can be provided in a constructor.
178 | * Provide a subclass `LabeledPoint` whose constructor takes a `label` value and `x` and `y`
179 | * coordinates, such as
180 | * {{{
181 | * new LabeledPoint("Black Thursday", 1929, 230.07)
182 | * }}}
183 | */
184 | class Point(val x: Double, val y: Double)
185 |
186 | class LabeledPoint(val label: String, x: Double, y: Double) extends Point(x, y)
187 |
188 | }
189 |
190 | package task0806 {
191 |
192 | import task0805.Point
193 |
194 | /**
195 | * Task 6:
196 | *
197 | * Define an abstract class `Shape` with an abstract method `centerPoint` and subclasses
198 | * `Rectangle` and `Circle`. Provide appropriate constructors for the subclasses and
199 | * override the `centerPoint` method in each subclass.
200 | */
201 | abstract class Shape {
202 | def centerPoint: Point
203 | }
204 |
205 | class Circle(override val centerPoint: Point, val radius: Double) extends Shape
206 |
207 | class Rectangle(val x1: Double, val y1: Double, val x2: Double, val y2: Double) extends Shape {
208 |
209 | override def centerPoint = new Point((x1 + x2) / 2, (y1 + y2) / 2)
210 | }
211 |
212 | }
213 |
214 | package task0807 {
215 |
216 | import java.awt.Rectangle
217 |
218 | /**
219 | * Task 7:
220 | *
221 | * Provide a class `Square` that extends `java.awt.Rectangle` and has three constructors:
222 | * one that constructs a square with a given corner point and width,
223 | * one that constructs a square with corner (0, 0) and a given width,
224 | * and one that constructs a square with corner (0, 0) and width 0.
225 | */
226 | class Square(x: Int = 0, y: Int = 0, width: Int = 0) extends Rectangle(x, y, width, width) {
227 |
228 | def this(width: Int) {
229 | this(0, 0, width)
230 | }
231 | }
232 |
233 | }
234 |
235 | package task0808 {
236 |
237 | /**
238 | * Task 8:
239 | *
240 | * Compile the `Person` and `SecretAgent` classes in Section 8.6, “Overriding Fields,” on page 91
241 | * and analyze the class files with `javap`.
242 | * How many name fields are there?
243 | * How many name getter methods are there?
244 | * What do they get?
245 | * (Hint: Use the -c and -private options.)
246 | */
247 | class Person(val name: String) {
248 | override def toString = getClass.getName + "[name=" + name + "]"
249 | }
250 |
251 | /**
252 | * Solution:
253 | *
254 | * There are two name fields, one in Person class, and one in SecretAgent class.
255 | * There are two name getter methods, one in each class as well. The second one overrides the first.
256 | * They get/return corresponding name field from their class.
257 | *
258 | * Additionally, there is separate toString field and overrided toString method, which returns
259 | * that field.
260 | */
261 | class SecretAgent(codename: String) extends Person(codename) {
262 | // Don’t want to reveal name...
263 | override val name = "secret"
264 | // ...or class name
265 | override val toString = "secret"
266 | }
267 |
268 | }
269 |
270 | /**
271 | * Task 9:
272 | *
273 | * In the `Creature` class of Section 8.10, "Construction Order and Early Definitions,"
274 | * on page 94, replace `val range` with a `def`. What happens when you also use a
275 | * `def` in the `Ant` subclass? What happens when you use a `val` in the subclass?
276 | * Why?
277 | */
278 | class Creature {
279 | def range: Int = 10
280 | val env: Array[Int] = new Array[Int](range)
281 | }
282 |
283 | class Ant extends Creature {
284 | override def range = 2
285 | }
286 |
287 | /**
288 | * Solution:
289 | *
290 | * When we use def in both superclass and subclass the array length is initialized with expected,
291 | * overrided value 2. This happens because there is no field to hold the value, and we don't have
292 | * initialization order problem.
293 | *
294 | * But when we use def in superclass and val in subclass initialization order problem is back
295 | * again. Since now the range value in subclass is backed by field and at the moment when
296 | * corresponding generated range getter method is called from superclass constructor the value is
297 | * not initialized yet and equals to 0 by default.
298 | */
299 | object Creatures extends App {
300 | println(new Ant().env.length)
301 | }
302 |
303 | /**
304 | * Task 10:
305 | *
306 | * The file `scala/collection/immutable/Stack.scala` contains the definition
307 | * {{{
308 | * class Stack[A] protected (protected val elems: List[A])
309 | * }}}
310 | * Explain the meanings of the `protected` keywords. (Hint: Review the discussion
311 | * of private constructors in Chapter 5.)
312 | *
313 | *
314 | * Solution:
315 | *
316 | * The first `protected` keyword defines protected primary constructor, which is accessible only
317 | * from auxiliary constructor or from subclass primary constructor.
318 | * The second `protected` keyword defines protected field `elems` with corresponding protected
319 | * getter method, they are accessible within class and subclasses.
320 | */
321 |
--------------------------------------------------------------------------------
/src/main/scala/Chapter09.scala:
--------------------------------------------------------------------------------
1 | import java.io._
2 | import java.nio.file._
3 | import java.nio.file.attribute.BasicFileAttributes
4 | import scala.collection.mutable.ArrayBuffer
5 | import scala.io.{BufferedSource, Source}
6 | import scala.language.implicitConversions
7 |
8 | object Chapter09 {
9 |
10 | /**
11 | * Task 1:
12 | *
13 | * Write a Scala code snippet that reverses the lines in a file
14 | * (making the last line the first one, and so on).
15 | */
16 | def reverseLines(file: File): Unit = {
17 | val source: BufferedSource = Source.fromFile(file)
18 | val lines = try {
19 | source.getLines().toBuffer.reverse
20 | }
21 | finally {
22 | source.close()
23 | }
24 |
25 | val writer = new PrintWriter(file)
26 | try {
27 | lines.foreach(writer.println)
28 | }
29 | finally {
30 | writer.close()
31 | }
32 | }
33 |
34 | /**
35 | * Task 2:
36 | *
37 | * Write a Scala program that reads a file with tabs, replaces each tab with spaces
38 | * so that tab stops are at n-column boundaries, and writes the result to the same file.
39 | */
40 | def replaceTabs(file: File, charsPerColumn: Int = 4): Unit = {
41 | var chars = 0
42 | val buff = new ArrayBuffer[Char]
43 | val source: BufferedSource = Source.fromFile(file)
44 | try for (c <- source) c match {
45 | case '\t' => for (_ <- 0 until (charsPerColumn - chars % charsPerColumn)) buff += ' '
46 | chars = 0
47 | case '\n' => buff += c
48 | chars = 0
49 | case _ => buff += c
50 | chars += 1
51 | }
52 | finally {
53 | source.close()
54 | }
55 |
56 | val writer = new PrintWriter(file)
57 | try {
58 | buff.foreach(writer.print)
59 | }
60 | finally {
61 | writer.close()
62 | }
63 | }
64 |
65 | /**
66 | * Task 3:
67 | *
68 | * Write a Scala code snippet that reads a file and prints all words with more than
69 | * 12 characters to the console. Extra credit if you can do this in a single line.
70 | */
71 | def printLongWords(file: String): Unit = {
72 | val maxWordLength: Int = 12
73 | Source.fromFile(file).mkString.split("\\s+").filter(_.length > maxWordLength).foreach(println)
74 | }
75 |
76 | /**
77 | * Task 4:
78 | *
79 | * Write a Scala program that reads a text file containing only floating-point numbers.
80 | * Print the sum, average, maximum, and minimum of the numbers in the file.
81 | */
82 | def printNumbersStat(file: String): Unit = {
83 | var count: Int = 0
84 | var sum: Double = 0.0
85 | var min: Double = Double.MaxValue
86 | var max: Double = Double.MinValue
87 | for (token <- Source.fromFile(file).mkString.split("\\s+"); num = token.toDouble) {
88 | count += 1
89 | sum += num
90 | if (num < min) min = num
91 | if (num > max) max = num
92 | }
93 |
94 | printf("sum: %.3f\n", sum)
95 | printf("average: %.3f\n", sum / count)
96 | printf("minimum: %.3f\n", min)
97 | printf("maximum: %.3f\n", max)
98 | }
99 |
100 | /**
101 | * Task 5:
102 | *
103 | * Write a Scala program that writes the powers of 2 and their reciprocals to a file,
104 | * with the exponent ranging from 0 to 20. Line up the columns:
105 | * {{{
106 | * 1 1
107 | * 2 0.5
108 | * 4 0.25
109 | * ... ...
110 | * }}}
111 | */
112 | def printPowersOf2(file: File): Unit = {
113 | val writer = new PrintWriter(file)
114 | try {
115 | for (i <- 0 to 20) {
116 | writer.println("%8.0f %f".format(math.pow(2.0, i), math.pow(2.0, -i)))
117 | }
118 | }
119 | finally {
120 | writer.close()
121 | }
122 | }
123 |
124 | /**
125 | * Task 6:
126 | *
127 | * Make a regular expression searching for quoted strings "like this, maybe with \" or \\"
128 | * in a Java or C++ program. Write a Scala program that prints out all such strings
129 | * in a source file.
130 | */
131 | def printQuotedStrings(file: String): Unit = {
132 | // got from here:
133 | // http://stackoverflow.com/questions/2498635/java-regex-for-matching-quoted-string-with-escaped-quotes
134 | val pattern = "\"(([^\\\\\"]+|\\\\([btnfr\"'\\\\]|[0-3]?[0-7]{1,2}|u[0-9a-fA-F]{4}))*)\"".r
135 |
136 | for (pattern(s, _, _) <- pattern.findAllIn(Source.fromFile(file).mkString)) {
137 | println(s)
138 | }
139 | }
140 |
141 | /**
142 | * Task 7:
143 | *
144 | * Write a Scala program that reads a text file and prints all tokens in the file
145 | * that are not floating-point numbers. Use a regular expression.
146 | */
147 | def printNonNumberTokens(file: String): Unit = {
148 | val pattern = "(?![\\d]+(\\.[\\d]+)?)\\w+".r
149 |
150 | for (s <- pattern.findAllIn(Source.fromFile(file).mkString)) {
151 | println(s)
152 | }
153 | }
154 |
155 | /**
156 | * Task 8:
157 | *
158 | * Write a Scala program that prints the `src` attributes of all `img` tags of a web page.
159 | * Use regular expressions and groups.
160 | */
161 | def printSrcOfImageTags(file: String): Unit = {
162 | val pattern = "(?i) {
178 | if (f.toString.endsWith(".class")) count += 1
179 | })
180 |
181 | count
182 | }
183 |
184 | implicit def makeFileVisitor(f: (Path) => Unit): FileVisitor[Path] = new SimpleFileVisitor[Path] {
185 | override def visitFile(p: Path, attrs: BasicFileAttributes) = {
186 | f(p)
187 | FileVisitResult.CONTINUE }
188 | }
189 |
190 | /**
191 | * Task 10:
192 | *
193 | * Expand the example with the serializable `Person` class that stores a collection of friends.
194 | * Construct a few `Person` objects, make some of them friends of another, and then
195 | * save an `Array[Person]` to a file. Read the array back in and verify that the friend
196 | * relations are intact.
197 | */
198 | class Person(val name: String) extends Serializable with Iterable[Person] {
199 |
200 | private val friends = new ArrayBuffer[Person]
201 |
202 | def addFriend(friend: Person): Person = {
203 | friends += friend
204 | this
205 | }
206 |
207 | override def iterator: Iterator[Person] = friends.toIterator
208 | }
209 |
210 | def savePersons(file: File, persons: Array[Person]): Unit = {
211 | val out = new ObjectOutputStream(new FileOutputStream(file))
212 | try {
213 | out.writeObject(persons)
214 | } finally {
215 | out.close()
216 | }
217 | }
218 |
219 | def readPersons(file: File): Array[Person] = {
220 | val in = new ObjectInputStream(new FileInputStream(file))
221 | try {
222 | in.readObject().asInstanceOf[Array[Person]]
223 | } finally {
224 | in.close()
225 | }
226 | }
227 | }
228 |
229 | object Chapter09PrintLongWordsApp extends Utils.FileApp(Chapter09.printLongWords)
230 |
231 | object Chapter09PrintNumbersStatApp extends Utils.FileApp(Chapter09.printNumbersStat)
232 |
233 | object Chapter09PrintQuotedStringsApp extends Utils.FileApp(Chapter09.printQuotedStrings)
234 |
235 | object Chapter09PrintNonNumberTokensApp extends Utils.FileApp(Chapter09.printNonNumberTokens)
236 |
237 | object Chapter09PrintSrcOfImageTagsApp extends Utils.FileApp(Chapter09.printSrcOfImageTags)
238 |
--------------------------------------------------------------------------------
/src/main/scala/Chapter12.scala:
--------------------------------------------------------------------------------
1 |
2 |
3 | object Chapter12 {
4 |
5 | /**
6 | * Task 1:
7 | *
8 | * Write a function `values(fun: (Int) => Int, low: Int, high: Int)` that yields a collection
9 | * of function inputs and outputs in a given range. For example, `values(x => x * x, -5, 5)`
10 | * should produce a collection of pairs `(-5, 25)`, `(-4, 16)`, `(-3, 9)`, ..., `(5, 25)`.
11 | */
12 | def values(fun: (Int) => Int, low: Int, high: Int): Seq[(Int, Int)] = {
13 | for (i <- low to high) yield (i, fun(i))
14 | }
15 |
16 | /**
17 | * Task 2:
18 | *
19 | * How do you get the largest element of an array with `reduceLeft`?
20 | */
21 | def largestElement(arr: Array[Int]): Int = arr.reduceLeft((a, b) => if (a > b) a else b)
22 |
23 | /**
24 | * Task 3:
25 | *
26 | * Implement the `factorial` function using `to` and `reduceLeft`, without a loop or recursion.
27 | */
28 | def factorial(n: Int): Int = if (n <= 0) 1 else (1 to n).reduceLeft(_ * _)
29 |
30 | /**
31 | * Task 4:
32 | *
33 | * The previous implementation needed a special case when `n < 1`. Show how you can avoid this
34 | * with `foldLeft`. (Look at the Scaladoc for `foldLeft`. It’s like `reduceLeft`, except that
35 | * the first value in the chain of combined values is supplied in the call.)
36 | */
37 | def factorial2(n: Int): Int = (1 to n).foldLeft(1)(_ * _)
38 |
39 | /**
40 | * Task 5:
41 | *
42 | * Write a function `largest(fun: (Int) => Int, inputs: Seq[Int])` that yields the largest
43 | * value of a function within a given sequence of inputs. For example,
44 | * `largest(x => 10 * x - x * x, 1 to 10)` should return `25`. Don't use a loop or recursion.
45 | */
46 | def largest(fun: (Int) => Int, inputs: Seq[Int]): Int = {
47 | inputs.map(fun(_)).reduceLeft((a, b) => if (a > b) a else b)
48 | }
49 |
50 | /**
51 | * Task 6:
52 | *
53 | * Modify the previous function to return the input at which the output is largest. For example,
54 | * `largestAt(x => 10 * x - x * x, 1 to 10)` should return `5`. Don't use a loop or recursion.
55 | */
56 | def largestAt(fun: (Int) => Int, inputs: Seq[Int]): Int = {
57 | val valueWithInput: (Int, Int) = inputs.map(i => (fun(i), i)).reduceLeft {(a, b) =>
58 | if (a._1 > b._1) a
59 | else b
60 | }
61 |
62 | valueWithInput._2
63 | }
64 |
65 | /**
66 | * Task 7:
67 | *
68 | * It's easy to get a sequence of pairs, for example
69 | * {{{
70 | * val pairs = (1 to 10) zip (11 to 20)
71 | * }}}
72 | * Now suppose you want to do something with such a sequence - say, add up the values.
73 | * But you can't do
74 | * {{{
75 | * pairs.map(_ + _)
76 | * }}}
77 | * The function `_ + _` takes two `Int` parameters, not an `(Int, Int)` pair. Write a function
78 | * `adjustToPair` that receives a function of type `(Int, Int) => Int` and returns the equivalent
79 | * function that operates on a pair. For example, `adjustToPair(_ * _)((6, 7))` is `42`.
80 | * Then use this function in conjunction with `map` to compute the sums of the elements in pairs.
81 | */
82 | def adjustToPair(fun: (Int, Int) => Int): ((Int, Int)) => Int = {
83 | (pair: (Int, Int)) => fun(pair._1, pair._2)
84 | }
85 |
86 | // Or using currying:
87 | //
88 | // def adjustToPair(fun: (Int, Int) => Int)(pair: (Int, Int)): Int = {
89 | // fun(pair._1, pair._2)
90 | // }
91 |
92 | def mapPairs(pairs: Seq[(Int, Int)], fun: (Int, Int) => Int): Seq[Int] = {
93 | pairs.map(adjustToPair(fun))
94 | }
95 |
96 | /**
97 | * Task 8:
98 | *
99 | * In Section 12.8, "Currying", on page 149, you saw the `corresponds` method used with two
100 | * arrays of strings. Make a call to corresponds that checks whether the elements in an
101 | * array of strings have the lengths given in an array of integers.
102 | */
103 | def correspondsLen(strings: Array[String], lengths: Array[Int]): Boolean = {
104 | strings.corresponds(lengths)(_.length == _)
105 | }
106 |
107 | /**
108 | * Task 9:
109 | *
110 | * Implement `corresponds` without currying. Then try the call from the preceding exercise.
111 | * What problem do you encounter?
112 | *
113 | * Solution:
114 | *
115 | * Without currying the compiler is not be able to infer the types for predicate function.
116 | * So, it should be called like this:
117 | * {{{
118 | * corresponds2(Array("a"), Array(1), (a: String, b: Int) => a.length == b)
119 | * }}}
120 | */
121 | def corresponds2[A, B](ax: Array[A], bx: Array[B], predicate: (A, B) => Boolean): Boolean = {
122 | ax.corresponds(bx)(predicate)
123 | }
124 |
125 | /**
126 | * Task 10:
127 | *
128 | * Implement an `unless` control abstraction that works just like `if`, but with an inverted
129 | * condition. Does the first parameter need to be a call-by-name parameter? Do you need currying?
130 | *
131 | * Solution:
132 | *
133 | * The first parameter not need to be a call-by-name parameter since it is evaluated only once.
134 | * Yes, we need currying, with it looks exactly like an `if` expression.
135 | */
136 | def unless(cond: Boolean)(block: => Unit): Unit = if (!cond) block
137 | }
138 |
--------------------------------------------------------------------------------
/src/main/scala/Chapter13.scala:
--------------------------------------------------------------------------------
1 | import scala.collection.{concurrent, mutable}
2 | import scala.io.Source
3 |
4 | object Chapter13 {
5 |
6 | /**
7 | * Task 1:
8 | *
9 | * Write a function that, given a string, produces a map of the indexes of all characters.
10 | * For example, `indexes("Mississippi")` should return a map associating
11 | * 'M' with the set {0},
12 | * ‘i’ with the set {1, 4, 7, 10}, and so on.
13 | * Use a mutable map of characters to mutable sets. How can you ensure that the set is sorted?
14 | *
15 | * Solution:
16 | *
17 | * We have to use `LinkedHashSet` to maintain the indices order in set.
18 | */
19 | def indexes(s: String): mutable.Map[Char, mutable.Set[Int]] = {
20 | val map = new mutable.HashMap[Char, mutable.Set[Int]]
21 | for (i <- 0 until s.length) {
22 | map.getOrElseUpdate(s(i), new mutable.LinkedHashSet[Int]) += i
23 | }
24 |
25 | map
26 | }
27 |
28 | /**
29 | * Task 2:
30 | *
31 | * Repeat the preceding exercise, using an immutable map of characters to lists.
32 | */
33 | def indexes2(s: String): Map[Char, List[Int]] = {
34 | var map = Map[Char, List[Int]]()
35 | for (i <- 0 until s.length) {
36 | val c = s(i)
37 | map = map.updated(c, map.getOrElse(c, Nil) :+ i)
38 | }
39 |
40 | map
41 | }
42 |
43 | /**
44 | * Task 3:
45 | *
46 | * Write a function that removes all zeroes from a linked list of integers.
47 | */
48 | type LinkedList[T] = mutable.LinkedList[T]
49 |
50 | def removeAllZeroes(list: LinkedList[Int]): LinkedList[Int] = {
51 | // remove elements at the beginning of the list
52 | var result = list
53 | while (result.nonEmpty && result.elem == 0) {
54 | result = result.next
55 | }
56 |
57 | // remove elements till the end of the list
58 | var prev, curr = result
59 | while (curr.nonEmpty) {
60 | if (curr.elem == 0) {
61 | prev.next = curr.next
62 | curr = prev
63 | }
64 |
65 | prev = curr
66 | curr = curr.next
67 | }
68 |
69 | result
70 | }
71 |
72 | /**
73 | * Task 4:
74 | *
75 | * Write a function that receives a collection of strings and a map from strings to integers.
76 | * Return a collection of integers that are values of the map corresponding to one of
77 | * the strings in the collection. For example, given
78 | * `Array("Tom", "Fred", "Harry")` and `Map("Tom" -> 3, "Dick" -> 4, "Harry" -> 5)`,
79 | * return `Array(3, 5)`. Hint: Use flatMap to combine the Option values returned by get.
80 | */
81 | def mapToValues(coll: Traversable[String], map: Map[String, Int]): Traversable[Int] = {
82 | coll.flatMap(map.get)
83 | }
84 |
85 | /**
86 | * Task 5:
87 | *
88 | * Implement a function that works just like `mkString`, using `reduceLeft`.
89 | */
90 | def collToString[T](xs: Traversable[T]): String = {
91 | if (xs.isEmpty) ""
92 | // we have to specify Any type here, since its the closest common parent type for String and T
93 | else xs.reduceLeft((a: Any, b: T) => a + ", " + b).toString
94 | }
95 |
96 | /**
97 | * Task 6:
98 | *
99 | * Given a list of integers `lst`, what is
100 | * `(lst :\ List[Int]())(_ :: _)` ?
101 | * `(List[Int]() /: lst)(_ :+ _)` ?
102 | * How can you modify one of them to reverse the list?
103 | *
104 | * Solution:
105 | *
106 | * The first expression executes `foldRight` and prepends the elements to the resulting list.
107 | * The second expression executes `foldLeft` and appends the elements to the resulting list.
108 | * Both expressions produce new `List[Int]` with the same elements as in the original list,
109 | * and in the same order.
110 | * To reverse the list its better to modify the second expression to prepend the elements,
111 | * which is cheaper for lists comparing to append.
112 | */
113 | def reversList(lst: List[Int]): List[Int] = {
114 | //(lst :\ List[Int]())((el, res) => res :+ el)
115 |
116 | (List[Int]() /: lst)((res, el) => el :: res)
117 | }
118 |
119 | /**
120 | * Task 7:
121 | *
122 | * In Section 13.11, "Zipping", on page 171, the expression
123 | * {{{
124 | * (prices zip quantities) map { p => p._1 * p._2 }
125 | * }}}
126 | * is a bit inelegant. We can't do
127 | * {{{
128 | * (prices zip quantities) map { _ * _ }
129 | * }}}
130 | * because `_ * _` is a function with two arguments, and we need a function with one argument
131 | * that is a tuple. The `tupled` method of the `Function` object changes a function with
132 | * two arguments to one that take a tuple. Apply `tupled` to the multiplication function
133 | * so you can map it over the list of pairs.
134 | */
135 | def multiply(prices: Iterable[Int], quantities: Iterable[Int]): Iterable[Int] = {
136 | (prices zip quantities) map Function.tupled { _ * _ }
137 | }
138 |
139 | /**
140 | * Task 8:
141 | *
142 | * Write a function that turns an array of `Double` values into a two-dimensional array.
143 | * Pass the number of columns as a parameter. For example, with `Array(1, 2, 3, 4, 5, 6)`
144 | * and three columns, return `Array(Array(1, 2, 3), Array(4, 5, 6))`. Use the `grouped` method.
145 | */
146 | def twoDimensionalArray(arr: Array[Double], columns: Int): Array[Array[Double]] = {
147 | require(arr.length % columns == 0,
148 | "array length should be compatible with the number of columns")
149 |
150 | //val res: Array[Array[Double]] = Array.ofDim(arr.length / columns, columns)
151 | val res = new mutable.ArrayBuffer[Array[Double]](arr.length / columns)
152 | for (row <- arr.grouped(columns)) {
153 | res += row
154 | }
155 |
156 | res.toArray
157 | }
158 |
159 | /**
160 | * Task 9:
161 | *
162 | * Harry Hacker writes a program that accepts a sequence of file names on the command line.
163 | * For each, he starts a new thread that reads the file and updates a letter frequency map
164 | * declared as
165 | * {{{
166 | * val frequencies = new scala.collection.mutable.HashMap[Char, Int] with
167 | * scala.collection.mutable.SynchronizedMap[Char, Int]
168 | * }}}
169 | * When reading a letter `c`, he calls
170 | * {{{
171 | * frequencies(c) = frequencies.getOrElse (c, 0) + 1
172 | * }}}
173 | * Why won't this work? Will it work if he used instead
174 | * {{{
175 | * import scala.collection.JavaConversions.asScalaConcurrentMap
176 | * val frequencies: scala.collection.mutable.ConcurrentMap[Char, Int] =
177 | * new java.util.concurrent.ConcurrentHashMap[Char, Int]
178 | * }}}
179 | *
180 | * Solution:
181 | *
182 | * It won't work with SynchronizedMap since its not synchronize addition operation.
183 | * And its not enough using ConcurrentHashMap, we also need to perform threadsafe addition.
184 | * See the fixed code below.
185 | */
186 | def getLetterFrequencyMap(files: Iterable[String]): Map[Char, Int] = {
187 | import scala.collection.JavaConversions.mapAsScalaConcurrentMap
188 |
189 | //val frequencies = new mutable.HashMap[Char, Int] with mutable.SynchronizedMap[Char, Int]
190 | val frequencies: concurrent.Map[Char, Int] =
191 | new java.util.concurrent.ConcurrentHashMap[Char, Int]
192 |
193 | val threads = files.map(file =>
194 | new Thread(new Runnable() {
195 | override def run() = {
196 | val source = Source.fromInputStream(getClass.getResourceAsStream(file))
197 | try {
198 | for (c <- source) {
199 | //frequencies(c) = frequencies.getOrElse(c, 0) + 1
200 | var incremented = false
201 | while (!incremented) {
202 | val oldVal = frequencies.putIfAbsent(c, 0).getOrElse(0)
203 | incremented = frequencies.replace(c, oldVal, oldVal + 1)
204 | }
205 | }
206 | }
207 | finally {
208 | source.close()
209 | }
210 | }
211 | }))
212 | threads.foreach(_.start())
213 | threads.foreach(_.join())
214 |
215 | frequencies.toMap
216 | }
217 |
218 | /**
219 | * Task 10:
220 | *
221 | * Harry Hacker reads a file into a string and wants to use a parallel collection to update
222 | * the letter frequencies concurrently on portions of the string. He uses the following code:
223 | * {{{
224 | * val frequencies = new scala.collection.mutable.HashMap[Char, Int]
225 | * for (c <- str.par) frequencies(c) = frequencies.getOrElse(c, 0) + 1
226 | * }}}
227 | * Why is this a terrible idea? How can he really parallelize the computation?
228 | * (Hint: Use aggregate.)
229 | *
230 | * Solution:
231 | *
232 | * Its bad, because with parallel computation we shouldn't mutate the shared data.
233 | */
234 | def getLetterFrequencyMap2(str: String): Map[Char, Int] = {
235 | str.par.aggregate(new mutable.HashMap[Char, Int])((freq, c) => {
236 | freq(c) = freq.getOrElse(c, 0) + 1
237 | freq
238 | }, (freq, freq2) => {
239 | for ((k, v) <- freq2) freq(k) = freq.getOrElse(k, 0) + v
240 | freq
241 | }).toMap
242 | }
243 | }
244 |
--------------------------------------------------------------------------------
/src/main/scala/Chapter14.scala:
--------------------------------------------------------------------------------
1 |
2 |
3 | object Chapter14 {
4 |
5 | /**
6 | * Task 1:
7 | *
8 | * Your Java Development Kit distribution has the source code for much of the JDK in the
9 | * `src.zip` file. Unzip and search for case labels (regular expression `case [^:]+:`).
10 | * Then look for comments starting with `//` and containing `[Ff]alls? thr` to catch comments
11 | * such as `// Falls through` or `// just fall thru`.
12 | * Assuming the JDK programmers follow the Java code convention, which requires such a comment,
13 | * what percentage of cases falls through?
14 | *
15 | * Solution:
16 | * {{{
17 | * mkdir /tmp/java-test/src
18 | * unzip /usr/lib/jvm/java-7-oracle/src.zip -d /tmp/java-test/src
19 | * cd /tmp/java-test
20 | * grep --include=*.java -r -E 'case [^:]+:' src > 1.txt
21 | * grep --include=*.java -r -E '//.*[Ff]alls? thr' src > 2.txt
22 | * }}}
23 | * As a result on my machine there is around 9500 lines in the first file and
24 | * 100 lines in the second file. Then:
25 | * {{{
26 | * (100 * 100%) / 9500 = 1%
27 | * }}}
28 | */
29 |
30 | /**
31 | * Task 2:
32 | *
33 | * Using pattern matching, write a function `swap` that receives a pair of integers and
34 | * returns the pair with the components swapped.
35 | */
36 | def swap(pair: (Int, Int)): (Int, Int) = pair match {
37 | case (one, two) => (two, one)
38 | }
39 |
40 | /**
41 | * Task 3:
42 | *
43 | * Using pattern matching, write a function `swap` that swaps the first two elements of
44 | * an array provided its length is at least two.
45 | */
46 | def swap2(arr: Array[Int]): Array[Int] = arr match {
47 | case Array(first, second, _*) =>
48 | arr(0) = second
49 | arr(1) = first
50 | arr
51 | case _ => arr
52 | }
53 |
54 | /**
55 | * Task 4:
56 | *
57 | * Add a case class `Multiple` that is a subclass of the `Item` class. For example,
58 | * `Multiple(10, Article("Blackwell Toaster", 29.95))` describes ten toasters. Of course,
59 | * you should be able to handle any items, such as bundles or multiples, in the second argument.
60 | * Extend the `price` function to handle this new case.
61 | */
62 | sealed abstract class Item
63 | case class Article(description: String, price: Double) extends Item
64 | case class Bundle(description: String, discount: Double, items: Item*) extends Item
65 | case class Multiple(count: Int, item: Item) extends Item
66 |
67 | def price(it: Item): Double = it match {
68 | case Article(_, p) => p
69 | case Bundle(_, disc, its @ _*) => its.map(price).sum - disc
70 | case Multiple(count, item) => count * price(item)
71 | }
72 |
73 | /**
74 | * Task 5:
75 | *
76 | * One can use lists to model trees that store values only in the leaves. For example, the list
77 | * `((3 8) 2 (5))` describes the tree
78 | * {{{
79 | * *
80 | * /|\
81 | * * 2 *
82 | * /\ |
83 | * 3 8 5
84 | * }}}
85 | * However, some of the list elements are numbers and others are lists. In Scala, you cannot
86 | * have heterogeneous lists, so you have to use a `List[Any]`. Write a `leafSum` function to
87 | * compute the sum of all elements in the leaves, using pattern matching to differentiate
88 | * between numbers and lists.
89 | */
90 | def leafSum(xs: List[Any]): Double = {
91 | xs.foldLeft(0.0) {(acc: Double, item) => item match {
92 | case n: Number => acc + n.doubleValue()
93 | case list: List[_] => acc + leafSum(list)
94 | case _ => acc
95 | }
96 | }
97 | }
98 |
99 | /**
100 | * Task 6:
101 | *
102 | * A better way of modeling such trees is with case classes. Let's start with binary trees.
103 | * {{{
104 | * sealed abstract class BinaryTree
105 | * case class Leaf(value: Int) extends BinaryTree
106 | * case class Node(left: BinaryTree, right: BinaryTree) extends BinaryTree
107 | * }}}
108 | * Write a function to compute the sum of all elements in the leaves.
109 | */
110 | object Task6 {
111 |
112 | sealed abstract class BinaryTree
113 |
114 | case class Leaf(value: Int) extends BinaryTree
115 |
116 | case class Node(left: BinaryTree, right: BinaryTree) extends BinaryTree
117 |
118 | def leafSum(bt: BinaryTree): Int = bt match {
119 | case leaf: Leaf => leaf.value
120 | case left Node right => leafSum(left) + leafSum(right)
121 | }
122 | }
123 |
124 | /**
125 | * Task 7:
126 | *
127 | * Extend the tree in the preceding exercise so that each node can have an arbitrary number
128 | * of children, and reimplement the `leafSum` function. The tree in exercise 5 should be as
129 | * {{{
130 | * Node(Node(Leaf(3), Leaf(8)), Leaf(2), Node(Leaf(5)))
131 | * }}}
132 | */
133 | object Task7 {
134 |
135 | sealed abstract class BinaryTree
136 |
137 | case class Leaf(value: Int) extends BinaryTree
138 |
139 | case class Node(children: BinaryTree*) extends BinaryTree
140 |
141 | def leafSum(bt: BinaryTree): Int = bt match {
142 | case leaf: Leaf => leaf.value
143 | case node: Node => node.children.foldLeft(0)((acc, item) => acc + leafSum(item))
144 | }
145 | }
146 |
147 | /**
148 | * Task 8:
149 | *
150 | * Extend the tree in the preceding exercise so that each non-leaf node stores an operator
151 | * in addition to the child nodes. Then write a function `eval` that computes the value.
152 | * For example, the tree
153 | * {{{
154 | * +
155 | * /|\
156 | * * 2 -
157 | * /\ |
158 | * 3 8 5
159 | * }}}
160 | * has value `(3 x 8) + 2 + (-5) = 21`.
161 | */
162 | object Task8 {
163 |
164 | sealed abstract class BinaryTree
165 |
166 | case class Leaf(value: Int) extends BinaryTree
167 |
168 | case class Node(op: Op, children: BinaryTree*) extends BinaryTree
169 |
170 | class Op private(val identity: Int, op: (Int, Int) => Int) {
171 | def apply(a: Int, b: Int): Int = op(a, b)
172 | }
173 |
174 | object Op {
175 | val Plus = new Op(0, _ + _)
176 | val Minus = new Op(0, _ - _)
177 | val Product = new Op(1, _ * _)
178 | }
179 |
180 | def eval(bt: BinaryTree): Int = bt match {
181 | case leaf: Leaf => leaf.value
182 | case node: Node => node.children.foldLeft(node.op.identity) { (acc, item) =>
183 | node.op(acc, eval(item))
184 | }
185 | }
186 | }
187 |
188 | /**
189 | * Task 9:
190 | *
191 | * Write a function that computes the sum of the non-None values in a `List[Option[Int]]`.
192 | * Don't use a match statement.
193 | */
194 | def sumOfNonNoneValues(xs: List[Option[Int]]): Int = xs.foldLeft(0) { (acc, item) =>
195 | acc + item.getOrElse(0)
196 | }
197 |
198 | /**
199 | * Task 10:
200 | *
201 | * Write a function that composes two functions of type `Double => Option[Double]`, yielding
202 | * another function of the same type. The composition should yield `None` if either function does.
203 | * For example,
204 | * {{{
205 | * def f(x: Double) = if (x >= 0) Some(sqrt(x)) else None
206 | * def g(x: Double) = if (x != 1) Some(1 / (x - 1)) else None
207 | * val h = compose(f, g)
208 | * }}}
209 | * Then `h(2)` is `Some(1)`, and `h(1)` and `h(0)` are `None`.
210 | */
211 | def compose(f1: Double => Option[Double], f2: Double => Option[Double]): Double => Option[Double] = {
212 | (x: Double) => f2(x) match {
213 | case Some(y) => f1(y)
214 | case None => None
215 | }
216 | }
217 | }
218 |
--------------------------------------------------------------------------------
/src/main/scala/Chapter15.scala:
--------------------------------------------------------------------------------
1 | import java.io.IOException
2 | import org.junit.Test
3 | import scala.annotation.{tailrec, varargs}
4 | import scala.io.Source
5 |
6 | object Chapter15 {
7 |
8 | /**
9 | * Task 1:
10 | *
11 | * Write four `JUnit` test cases that use the `@Test` annotation with and without each of
12 | * its arguments. Run the tests with JUnit.
13 | */
14 | class Task1 {
15 |
16 | @Test
17 | def testCase1(): Unit = {
18 | }
19 |
20 | @Test(timeout = 500)
21 | def testCase2(): Unit = {
22 | }
23 |
24 | @Test(expected = classOf[RuntimeException])
25 | def testCase3(): Unit = {
26 | throw new RuntimeException()
27 | }
28 |
29 | @Test(timeout = 500, expected = classOf[RuntimeException])
30 | def testCase4(): Unit = {
31 | throw new RuntimeException()
32 | }
33 | }
34 |
35 | /**
36 | * Task 2:
37 | *
38 | * Make an example class that shows every possible position of an annotation.
39 | * Use `@deprecated` as your sample annotation.
40 | *
41 | * Solution:
42 | *
43 | * I've used `@unchecked` instead to avoid compilation warnings, since `@deprecated` now takes
44 | * two arguments.
45 | */
46 | @unchecked
47 | class Deprecated[@unchecked T] @unchecked() (@unchecked val field: Int) {
48 |
49 | @unchecked
50 | type Dep = Int @unchecked
51 |
52 | @unchecked
53 | var field2: Int @unchecked = 0
54 |
55 | @unchecked
56 | def unchecked(@deprecatedName('oldArg) newArg: Int): Int = {
57 | // annotation on an expression
58 | 1 + 2: @unchecked
59 | }
60 | }
61 |
62 | /**
63 | * Task 3:
64 | *
65 | * Which annotations from the Scala library use one of the meta-annotations
66 | * {{{
67 | * @param, @field, @getter, @setter, @beanGetter, or @beanSetter?
68 | * }}}
69 | *
70 | * Solution:
71 | *
72 | * - `@deprecated`
73 | * - `@deprecatedName`
74 | * - `@BeanProperty`
75 | */
76 |
77 | /**
78 | * Task 4:
79 | *
80 | * Write a Scala method `sum` with variable integer arguments that returns the sum of its
81 | * arguments. Call it from Java.
82 | *
83 | * @see Chapter15Task4.java
84 | */
85 | @varargs
86 | def sum(args: Int*): Int = {
87 | args.sum
88 | }
89 |
90 | /**
91 | * Task 5:
92 | *
93 | * Write a Scala method that returns a string containing all lines of a file. Call it from Java.
94 | */
95 | @throws[IOException]
96 | def fileToString(file: String): String = {
97 | val inStream = getClass.getResourceAsStream(file)
98 | if (inStream == null) {
99 | throw new IOException("Resource is not found: " + file)
100 | }
101 |
102 | Source.fromInputStream(inStream).mkString
103 | }
104 |
105 | /**
106 | * Task 6:
107 | *
108 | * Write a Scala object with a `volatile` `Boolean` field. Have one thread sleep for some time,
109 | * then set the field to `true`, print a message, and exit. Another thread will keep checking
110 | * whether the field is `true`. If so, it prints a message and exits. If not, it sleeps for
111 | * a short time and tries again. What happens if the variable is not volatile?
112 | *
113 | * Solution:
114 | *
115 | * If the variable is not volatile, then threads may see the changes to this variable with some
116 | * delay, or may not see changes at all. Since non-volatile (normal) variables may be cached.
117 | */
118 | object Work {
119 |
120 | @volatile
121 | private var done = false
122 |
123 | def doWork(): Unit = {
124 | val threads = List(new Thread(new Runnable {
125 |
126 | override def run(): Unit = {
127 | Thread.sleep(200)
128 |
129 | Work.done = true
130 | println("Work.done flag was set to true")
131 | }
132 | }), new Thread(new Runnable {
133 |
134 | override def run(): Unit = {
135 | while (!Work.done) {
136 | println("Work is not done yet, waiting...")
137 | Thread.sleep(100)
138 | }
139 |
140 | println("Work is done, exiting")
141 | }
142 | }))
143 |
144 | threads.foreach(_.start())
145 | threads.foreach(_.join())
146 | }
147 | }
148 |
149 | /**
150 | * Task 7:
151 | *
152 | * Give an example to show that the tail recursion optimization is not valid when a method
153 | * can be overridden.
154 | *
155 | * Solution:
156 | *
157 | * To be tail recursive the method in class should be either `private` or `final`.
158 | */
159 | class TailRecursion {
160 |
161 | @tailrec
162 | final def sum(xs: List[Int], acc: Int = 0): Int = xs match {
163 | case head :: tail => sum(tail, acc + head)
164 | case _ => acc
165 | }
166 | }
167 |
168 | /**
169 | * Task 8:
170 | *
171 | * Add the `allDifferent` method to an object, compile and look at the bytecode.
172 | * What methods did the `@specialized` annotation generate?
173 | */
174 | def allDifferent[@specialized T](x: T, y: T, z: T): Boolean = x != y && x != z && y != z
175 |
176 | /**
177 | * Task 9:
178 | *
179 | * The `Range.foreach` method is annotated as `@specialized(Unit)`. Why?
180 | * Look at the bytecode by running
181 | * {{{
182 | * javap -classpath /path/to/scala/lib/scala-library.jar scala.collection.immutable.Range
183 | * }}}
184 | * and consider the `@specialized` annotations on `Function1`. Click on the `Function1.scala`
185 | * link in Scaladoc to see them.
186 | *
187 | * Solution:
188 | *
189 | * In the bytecode for my scala library version (2.11.7):
190 | * {{{
191 | * public final void foreach$mVc$sp(scala.Function1
33 | *
38 | * }}}
39 | * How do you fix it?
40 | *
41 | * Solution:
42 | *
43 | * The given snippet produces an error in the third `li` element: "No closing Tag", because
44 | * the brace `{` symbol is interpreted by Scala compiler as start of Scala expression.
45 | * To fix it we can escape braces by using '{{' and '}}'.
46 | *
47 | * @see Chapter16Spec.scala
48 | */
49 |
50 | /**
51 | * Task 3:
52 | *
53 | * Contrast
54 | * {{{
55 | *
`.
164 | */
165 | def mapToXml(map: Map[String, String]): Elem = {
166 | {
167 | for ((key, value) <- map) yield {
168 |
172 | }
173 |
174 | /**
175 | * Task 8:
176 | *
177 | * Write a function that takes a `dl` element and turns it into a `Map[String, String]`.
178 | * This function should be the inverse of the function in the preceding exercise, provided
179 | * all `dt` children are distinct.
180 | */
181 | def xmlToMap(elem: Elem): Map[String, String] = elem match {
182 | case {children @ _*}
=>
183 | val map = new mutable.HashMap[String, String]
184 | var currKey = ""
185 | for (child <- children) child match {
186 | case if e.attribute("alt").isEmpty || e.attributes("alt").text.isEmpty =>
205 | e.asInstanceOf[Elem] % Attribute(null, "alt", "TODO", Null)
206 | case _ => n
207 | }
208 | }
209 |
210 | new RuleTransformer(rule).transform(root).head
211 | }
212 |
213 | /**
214 | * Task 10:
215 | *
216 | * Write a function that reads an XHTML document, carries out the transformation of
217 | * the preceding exercise, and saves the result.
218 | * Be sure to preserve the DTD and any CDATA sections.
219 | */
220 | def transformXhtmlFile(inFile: String, outFile: String): Unit = {
221 | val doc = new XhtmlParser(scala.io.Source.fromInputStream(
222 | getClass.getResourceAsStream(inFile))).initialize.document()
223 |
224 | val xhtml = transformXhtml(doc.docElem)
225 | val dtd = doc.dtd
226 | XML.save(outFile, xhtml, enc = "UTF-8",
227 | doctype = DocType(xhtml.label, dtd.externalID, dtd.decls))
228 | }
229 | }
230 |
231 | object Chapter16PrintImgWithoutAltApp extends Utils.FileApp(Chapter16.printImgWithoutAlt)
232 |
233 | object Chapter16PrintAllImgApp extends Utils.FileApp(Chapter16.printAllImg)
234 |
235 | object Chapter16PrintAllHyperlinksApp extends Utils.FileApp(Chapter16.printAllHyperlinks)
236 |
--------------------------------------------------------------------------------
/src/main/scala/Chapter17.scala:
--------------------------------------------------------------------------------
1 |
2 |
3 | /**
4 | * Task 1:
5 | *
6 | * Define an immutable `class Pair[T, S]` with a method `swap` that returns a new pair with
7 | * the components swapped.
8 | */
9 | package task1701 {
10 |
11 | class Pair[T, S](val first: T, val second: S) {
12 |
13 | def swap(): Pair[S, T] = new Pair(second, first)
14 | }
15 |
16 | }
17 |
18 | /**
19 | * Task 2:
20 | *
21 | * Define a mutable `class Pair[T]` with a method `swap` that swaps the components of the pair.
22 | */
23 | package task1702 {
24 |
25 | class Pair[T](var first: T, var second: T) {
26 |
27 | def swap(): Unit = {
28 | val tmp = first
29 | first = second
30 | second = tmp
31 | }
32 | }
33 |
34 | }
35 |
36 | /**
37 | * Task 3:
38 | *
39 | * Given a `class Pair[T, S]`, write a generic method `swap` that takes a pair as its argument
40 | * and returns a new pair with the components swapped.
41 | */
42 | object Chapter17Task03 {
43 |
44 | import task1701._
45 |
46 | def swap[T, S](pair: Pair[T, S]): Pair[S, T] = new Pair(pair.second, pair.first)
47 |
48 | }
49 |
50 | /**
51 | * Task 4:
52 | *
53 | * Why don't we need a lower bound for the `replaceFirst` method in Section 17.3,
54 | * "Bounds for Type Variables”, on page 232 if we want to replace the first component of
55 | * a `Pair[Person]` with a `Student`?
56 | *
57 | * Solution:
58 | *
59 | * We don't need a lower bound because we replacing with a sub-class, which is OK, since
60 | * the result type is still `Pair[Person]`.
61 | */
62 | package task1704 {
63 |
64 | class Person(val name: String)
65 | class Student(name: String) extends Person(name)
66 |
67 | class Pair[T](val first: T, val second: T) {
68 |
69 | def replaceFirst(newFirst: T): Pair[T] = new Pair(newFirst, second)
70 | }
71 |
72 | }
73 |
74 | /**
75 | * Task 5:
76 | *
77 | * Why does `RichInt` implement `Comparable[Int]` and not `Comparable[RichInt]`?
78 | *
79 | * Solution:
80 | *
81 | * To be able to use view bounds, like
82 | * {{{
83 | * class Pair[T <% Comparable[T]](val first: T, val second: T)
84 | * }}}
85 | * which than can be used with `Int` types, like
86 | * {{{
87 | * new Pair(1, 2)
88 | * }}}
89 | * we need to have implicit conversion from `T` to `Comparable[T]`. `RichInt` class
90 | * implements `Comparable[Int]` and there is implicit conversion from `Int` to `RichInt`.
91 | * So, we don't use `RichInt` class directly.
92 | */
93 |
94 | /**
95 | * Task 6:
96 | *
97 | * Write a generic method `middle` that returns the middle element from any `Iterable[T]`.
98 | * For example, `middle("World")` is 'r'.
99 | */
100 | object Chapter17Task06 {
101 |
102 | //def middle[T](xs: Iterable[T]): Option[T] = {
103 | //def middle[A, C](xs: C)(implicit ev: C <:< Iterable[A]): Option[A] = {
104 | //def middle[A, C <% Iterable[A]](xs: C): Option[A] = {
105 | def middle[A, C](xs: C)(implicit ev: C => Iterable[A]): Option[A] = {
106 | val size = xs.size
107 | if (size % 2 == 0) {
108 | return None
109 | }
110 |
111 | var distance = size / 2
112 | xs.find { _ =>
113 | val found = if (distance == 0) true else false
114 | distance -= 1
115 | found
116 | }
117 | }
118 | }
119 |
120 | /**
121 | * Task 7:
122 | *
123 | * Look through the methods of the `Iterable[+A]` trait. Which methods use the type parameter `A`?
124 | * Why is it in a covariant position in these methods?
125 | *
126 | * Solution:
127 | *
128 | * Parameter `A` used in the following methods, for example: `head`, `last`, `min`, `max`.
129 | * Its used in a covariant position since its defined with the covariant variance annotation (+A).
130 | */
131 |
132 | /**
133 | * Task 8:
134 | *
135 | * In Section 17.10, "Co- and Contravariant Positions", on page 238, the `replaceFirst` method
136 | * has a type bound. Why can't you define an equivalent method on a mutable `Pair[T]`?
137 | * {{{
138 | * def replaceFirst[R >: T](newFirst: R) { first = newFirst } // Error
139 | * }}}
140 | *
141 | * Solution:
142 | *
143 | * It is an error because the used type bound (`R >: T`) allows passing supper type instances.
144 | * But we can change the type bound definition to allow passing sub type instances:
145 | * {{{
146 | * def replaceFirst[R <: T](newFirst: R) { first = newFirst }
147 | * }}}
148 | */
149 |
150 | /**
151 | * Task 9:
152 | *
153 | * It may seem strange to restrict method parameters in an immutable `class Pair[+T]`. However,
154 | * suppose you could define
155 | * {{{
156 | * def replaceFirst(newFirst: T)
157 | * }}}
158 | * in a `Pair[+T]`. The problem is that this method can be overridden in an unsound way.
159 | * Construct an example of the problem. Define a subclass `NastyDoublePair` of `Pair[Double]`
160 | * that overrides `replaceFirst` so that it makes a pair with the square root of `newFirst`.
161 | * Then construct the call `replaceFirst("Hello")` on a `Pair[Any]` that is actually
162 | * a `NastyDoublePair`.
163 | */
164 | object Chapter17Task09 {
165 |
166 | class Pair[+T](val first: T, val second: T) {
167 |
168 | //def replaceFirst(newFirst: T): Pair[T] = new Pair(newFirst, second)
169 |
170 | def replaceFirst[R >: T](newFirst: R): Pair[R] = new Pair(newFirst, second)
171 | }
172 |
173 | class NastyDoublePair(first: Double, second: Double) extends Pair[Double](first, second) {
174 |
175 | //override def replaceFirst[R >: Double](newFirst: R) = new Pair(math.sqrt(newFirst), second)
176 |
177 | override def replaceFirst[R >: Double](newFirst: R) = new Pair(math.sqrt(first), second)
178 | }
179 |
180 | def check(pair: Pair[Any]): Pair[Any] = pair.replaceFirst("Hello")
181 | }
182 |
183 | /**
184 | * Task 10:
185 | *
186 | * Given a mutable `Pair[S, T]` class, use a type constraint to define a `swap` method that can
187 | * be called if the type parameters are the same.
188 | */
189 | package task1710 {
190 |
191 | class Pair[S, T](var first: S, var second: T) {
192 |
193 | def swap(implicit ev: T =:= S): Unit = {
194 | val tmp = first.asInstanceOf[T]
195 | first = second
196 | second = tmp
197 | }
198 | }
199 |
200 | }
201 |
--------------------------------------------------------------------------------
/src/main/scala/Chapter18.scala:
--------------------------------------------------------------------------------
1 | import scala.beans.BeanProperty
2 | import scala.language.reflectiveCalls
3 |
4 | object Chapter18 {
5 |
6 | /**
7 | * Task 1:
8 | *
9 | * Implement a `Bug` class modeling a bug that moves along a horizontal line.
10 | * The `move` method moves in the current direction,
11 | * the `turn` method makes the bug turn around,
12 | * and the `show` method prints the current position.
13 | * Make these methods chainable. For example,
14 | * {{{
15 | * bugsy.move(4).show().move(6).show().turn().move(5).show()
16 | * }}}
17 | * should display `4 10 5`.
18 | */
19 | class Bug {
20 |
21 | private var position: Int = 0
22 | private var turnedAround: Boolean = false
23 |
24 | def move(steps: Int): this.type = {
25 | if (turnedAround) position -= steps
26 | else position += steps
27 | this
28 | }
29 |
30 | def turn(): this.type = {
31 | turnedAround = !turnedAround
32 | this
33 | }
34 |
35 | def show(): this.type = {
36 | print(" ")
37 | print(position)
38 | this
39 | }
40 | }
41 |
42 | /**
43 | * Task 2:
44 | *
45 | * Provide a fluent interface for the `Bug` class of the preceding exercise, so that one can
46 | * write
47 | * {{{
48 | * bugsy move 4 and show and then move 6 and show turn around move 5 and show
49 | * }}}
50 | */
51 | trait FluentBug { this: Bug =>
52 |
53 | def and(obj: Show.type): this.type = this.show()
54 |
55 | def and(obj: Then.type): this.type = this
56 |
57 | def turn(obj: Around.type): this.type = this.turn()
58 | }
59 |
60 | object Show
61 | object Then
62 | object Around
63 |
64 | val show = Show
65 | val then = Then
66 | val around = Around
67 |
68 | /**
69 | * Task 3:
70 | *
71 | * Complete the fluent interface in Section 18.1, "Singleton Types", on page 246
72 | * so that one can call
73 | * {{{
74 | * book set Title to "Scala for the Impatient" set Author to "Cay Horstmann"
75 | * }}}
76 | */
77 | trait FluentDocument { this: Document =>
78 |
79 | private var useNextArgsAs: Option[Any] = None
80 |
81 | def set(obj: Title.type): this.type = setNextArgsAs(obj)
82 | def set(obj: Author.type): this.type = setNextArgsAs(obj)
83 |
84 | def to(arg: String): this.type = {
85 | for (obj <- useNextArgsAs) obj match {
86 | case Title => setTitle(arg)
87 | case Author => setAuthor(arg)
88 | }
89 |
90 | this
91 | }
92 |
93 | private def setNextArgsAs(obj: Any): this.type = {
94 | useNextArgsAs = Some(obj)
95 | this
96 | }
97 | }
98 |
99 | object Title
100 | object Author
101 |
102 | class Document {
103 |
104 | @BeanProperty var title: String = null
105 | @BeanProperty var author: String = null
106 | }
107 |
108 | class Book extends Document with FluentDocument {
109 |
110 | def addChapter(chapter: String): this.type = this
111 | }
112 |
113 | /**
114 | * Task 4:
115 | *
116 | * Implement the `equals` method for the `Member` class that is nested inside the `Network`
117 | * class in Section 18.2, "Type Projections", on page 247. For two members to be equal,
118 | * they need to be in the same network.
119 | */
120 | class Network {
121 |
122 | class Member {
123 |
124 | override def equals(that: Any): Boolean = that match {
125 | case _: Member => true
126 | case _ => false
127 | }
128 | }
129 | }
130 |
131 | /**
132 | * Task 5:
133 | *
134 | * Consider the type alias
135 | * {{{
136 | * type NetworkMember = n.Member forSome { val n: Network }
137 | * }}}
138 | * and the function
139 | * {{{
140 | * def process(m1: NetworkMember, m2: NetworkMember) = (m1, m2)
141 | * }}}
142 | * How does this differ from the `process` function in Section 18.8, "Existential Types",
143 | * on page 252?
144 | *
145 | * Solution:
146 | *
147 | * The difference is that `NetworkMember` defines type alias for `Member` from any `Network`,
148 | * which is the same as defining `process` function like this:
149 | * {{{
150 | * def process[M >: n.Member forSome { val n: Network }](m1: M, m2: M) = (m1, m2)
151 | * }}}
152 | * While the `process` function, from Section 18.8, accepts only members from the same network,
153 | * and it is defined like this:
154 | * {{{
155 | * def process[M <: n.Member forSome { val n: Network }](m1: M, m2: M) = (m1, m2)
156 | * }}}
157 | */
158 | import scala.language.existentials
159 |
160 | type NetworkMember = n.Member forSome { val n: Network }
161 |
162 | def process(m1: NetworkMember, m2: NetworkMember) = (m1, m2)
163 |
164 | def processAny[M >: n.Member forSome { val n: Network }](m1: M, m2: M) = (m1, m2)
165 |
166 | def processSame[M <: n.Member forSome { val n: Network }](m1: M, m2: M) = (m1, m2)
167 |
168 | /**
169 | * Task 6:
170 | *
171 | * The `Either` type in the Scala library can be used for algorithms that return either a result
172 | * or some failure information. Write a function that takes two parameters:
173 | * a sorted array of integers and an integer value.
174 | * Return either the index of the value in the array or the index of the element that is closest
175 | * to the value. Use an infix type as the return type.
176 | */
177 | def findClosestValueIndex(sortedArr: Array[Int], value: Int): Int Either Int = {
178 | var i = 0
179 | while (i < sortedArr.length) {
180 | val elem = sortedArr(i)
181 | if (elem == value) return Left(i)
182 | else if (elem > value) return Right(i)
183 | i += 1
184 | }
185 |
186 | // return index if the last element
187 | Right(i - 1)
188 | }
189 |
190 | /**
191 | * Task 7:
192 | *
193 | * Implement a method that receives an object of any class that has a method
194 | * {{{
195 | * def close(): Unit
196 | * }}}
197 | * together with a function that processes that object. Call the function and invoke the
198 | * `close` method upon completion, or when any exception occurs.
199 | */
200 | def processAndClose[T <: { def close(): Unit }, R](obj: T)(f: T => R): R = {
201 | try {
202 | f(obj)
203 | }
204 | finally {
205 | obj.close()
206 | }
207 | }
208 |
209 | /**
210 | * Task 8:
211 | *
212 | * Write a function `printValues` with three parameters `f`, `from`, `to` that prints all values
213 | * of `f` with inputs from the given range. Here, `f` should be any object with an `apply` method
214 | * that consumes and yields an `Int`. For example,
215 | * {{{
216 | * printValues((x: Int) => x * x, 3, 6) // Prints 9 16 25 36
217 | * printValues(Array(1, 1, 2, 3, 5, 8, 13, 21, 34, 55), 3, 6) // Prints 3 5 8 13
218 | * }}}
219 | */
220 | def printValues(f: { def apply(i: Int): Int }, from: Int, to: Int): Unit = {
221 | var i = from
222 | while (i <= to) {
223 | print(" ")
224 | print(f(i))
225 | i += 1
226 | }
227 | }
228 |
229 | /**
230 | * Task 9:
231 | *
232 | * Consider this class that models a physical dimension:
233 | * {{{
234 | * abstract class Dim[T](val value: Double, val name: String) {
235 | * protected def create(v: Double): T
236 | * def +(other: Dim[T]) = create(value + other.value)
237 | * override def toString() = value + " " + name
238 | * }
239 | * }}}
240 | * Here is a concrete subclass:
241 | * {{{
242 | * class Seconds(v: Double) extends Dim[Seconds](v, "s") {
243 | * override def create(v: Double) = new Seconds(v)
244 | * }
245 | * }}}
246 | * But now a knucklehead could define
247 | * {{{
248 | * class Meters(v: Double) extends Dim[Seconds](v, "m") {
249 | * override def create(v: Double) = new Seconds(v)
250 | * }
251 | * }}}
252 | * allowing meters and seconds to be added. Use a self type to prevent that.
253 | */
254 | abstract class Dim[T](val value: Double, val name: String) { this: T =>
255 |
256 | protected def create(v: Double): T
257 |
258 | def +(other: Dim[T]) = create(value + other.value)
259 |
260 | override def toString = value + " " + name
261 | }
262 |
263 | class Seconds(v: Double) extends Dim[Seconds](v, "s") {
264 | override def create(v: Double) = new Seconds(v)
265 | }
266 |
267 | class Meters(v: Double) extends Dim[Meters](v, "m") {
268 | override def create(v: Double) = new Meters(v)
269 | }
270 | }
271 |
272 | /**
273 | * Task 10:
274 | *
275 | * Self types can usually be replaced with traits that extend classes, but there can be
276 | * situations where using self types changes the initialization and override orders.
277 | * Construct such an example.
278 | */
279 | package task1810 {
280 |
281 | class A(val name: String) {
282 |
283 | override def toString: String = name
284 | }
285 |
286 | trait Named { self: A =>
287 |
288 | override val name: String = "Named: " + self.name
289 |
290 | override def toString: String = "from Named: " + super.toString
291 | }
292 | }
293 |
--------------------------------------------------------------------------------
/src/main/scala/Chapter21.scala:
--------------------------------------------------------------------------------
1 | import Chapter11.Fraction
2 | import java.awt.Point
3 | import scala.annotation.tailrec
4 | import scala.collection.immutable.IndexedSeq
5 | import scala.io.StdIn
6 | import scala.language.implicitConversions
7 |
8 |
9 | object Chapter21 {
10 |
11 | /**
12 | * Task 1:
13 | *
14 | * How does `->` work? That is, how can `"Hello" -> 42` and `42 -> "Hello"` be pairs
15 | * `("Hello", 42)` and `(42, "Hello")`? Hint: `Predef.any2ArrowAssoc`.
16 | *
17 | * Solution:
18 | *
19 | * Currently, as of Scala 2.11.x, its implemented by using `implicit class ArrowAssoc` that
20 | * enriches any instance with `->` method.
21 | */
22 |
23 | /**
24 | * Task 2:
25 | *
26 | * Define an operator `+%` that adds a given percentage to a value. For example,
27 | * `120 +% 10` should be `132`. Hint: Since operators are methods, not functions,
28 | * you will also need to provide an `implicit`.
29 | */
30 | implicit class PercentAdder(private val value: Int) {
31 |
32 | def +%(percent: Int): Int = value + ((value * percent) / 100d).toInt
33 | }
34 |
35 | // as of Scala 2.11.x not needed any more, since we can use implicit class
36 | //implicit def int2PercentAdder(value: Int): PercentAdder = new PercentAdder(value)
37 |
38 | /**
39 | * Task 3:
40 | *
41 | * Define a `!` operator that computes the factorial of an integer. For example,
42 | * `5!` is `120`. You will need an enrichment class and an implicit conversion.
43 | */
44 | implicit class Int2Factorial(private val value: Int) {
45 |
46 | def ! : Int = {
47 | @tailrec
48 | def fact(acc: Int, n: Int): Int = {
49 | if (n == 1) acc
50 | else fact(acc * n, n - 1)
51 | }
52 |
53 | fact(1, value)
54 | }
55 | }
56 |
57 | /**
58 | * Task 4:
59 | *
60 | * Some people are fond of "fluent APIs" that read vaguely like English sentences.
61 | * Create such an API for reading integers, floating-point numbers, and strings from the console.
62 | * For example:
63 | * {{{
64 | * Read in aString askingFor "Your name" and
65 | * anInt askingFor "Your age" and
66 | * aDouble askingFor "Your weight".
67 | * }}}
68 | */
69 | sealed trait FluentFieldType
70 |
71 | object aString extends FluentFieldType
72 |
73 | object anInt extends FluentFieldType
74 |
75 | object aDouble extends FluentFieldType
76 |
77 | def Read: FluentReader = new FluentReader
78 |
79 | class FluentReader {
80 |
81 | private var data: List[(String, Any)] = List()
82 |
83 | private var nextType: FluentFieldType = aString
84 |
85 | def getData: List[(String, Any)] = data
86 |
87 | def in(fieldType: FluentFieldType): FluentReader = {
88 | nextType = fieldType
89 | this
90 | }
91 |
92 | def and(fieldType: FluentFieldType): FluentReader = in(fieldType)
93 |
94 | def askingFor(fieldName: String): FluentReader = {
95 | print(fieldName + ": ")
96 |
97 | val value = nextType match {
98 | case _: aString.type => StdIn.readLine()
99 | case _: anInt.type => StdIn.readInt()
100 | case _: aDouble.type => StdIn.readDouble()
101 | }
102 |
103 | data = data :+ (fieldName, value)
104 | this
105 | }
106 | }
107 |
108 | /**
109 | * Task 5:
110 | *
111 | * Provide the machinery that is needed to compute
112 | * {{{
113 | * smaller(Fraction(1, 7), Fraction(2, 9))
114 | * }}}
115 | * in Section 21.6, "Implicit Conversions with Implicit Parameters," on page 310.
116 | * Supply a `class RichFraction` that extends `Ordered[Fraction]`.
117 | */
118 | def smaller[T](a: T, b: T)(implicit order: T => Ordered[T]): T =
119 | if (order(a) < b) a
120 | else b
121 |
122 | implicit class RichFraction(self: Fraction) extends Ordered[Fraction] {
123 |
124 | override def compare(that: Fraction): Int = (self - that).num
125 | }
126 |
127 | /**
128 | * Task 6:
129 | *
130 | * Compare objects of the `class java.awt.Point` by lexicographic comparison.
131 | */
132 | implicit class LexicographicPointOrdering(self: Point) extends Ordered[Point] {
133 |
134 | override def compare(that: Point): Int =
135 | if (self.x == that.x) self.y - that.y
136 | else self.x - that.x
137 | }
138 |
139 | /**
140 | * Task 7:
141 | *
142 | * Continue the previous exercise, comparing two points according to their distance to
143 | * the origin. How can you switch between the two orderings?
144 | *
145 | * Solution:
146 | *
147 | * We can switch between the two orderings by importing appropriate `implicit class`:
148 | * {{{
149 | * import Chapter21.LexicographicPointOrdering
150 | * }}}
151 | * or
152 | * {{{
153 | * import Chapter21.DistancePointOrdering
154 | * }}}
155 | */
156 | implicit class DistancePointOrdering(self: Point) extends Ordered[Point] {
157 |
158 | override def compare(that: Point): Int = {
159 | val d1 = self.distance(0, 0)
160 | val d2 = that.distance(0, 0)
161 |
162 | if (d1 < d2) -1
163 | else if (d1 > d2) 1
164 | else 0
165 | }
166 | }
167 |
168 | /**
169 | * Task 8:
170 | *
171 | * Use the `implicitly` command in the REPL to summon the implicit objects described in
172 | * Section 21.5, "Implicit Parameters," on page 309 and
173 | * Section 21.6, "Implicit Conversions with Implicit Parameters," on page 310.
174 | * What objects do you get?
175 | *
176 | * Solution:
177 | *
178 | * To start the REPL console, start the following command in the project root directory:
179 | * {{{
180 | * activator console
181 | * }}}
182 | * Here is the REPL output:
183 | * {{{
184 | * scala> import Chapter21._
185 | * import Chapter21._
186 | *
187 | * scala> import Chapter11.Fraction
188 | * import Chapter11.Fraction
189 | *
190 | * scala> implicitly[Delimiters]
191 | * res0: Chapter21.Delimiters = Delimiters(<<,>>)
192 | *
193 | * scala> implicitly[Ordered[Fraction]]
194 | *
137 | |
138 | |
139 | |
140 | |
141 | |
142 | |""".stripMargin)
143 |
144 | //when
145 | val (exit, out, err) = runApp("Chapter09PrintSrcOfImageTagsApp", file.getAbsolutePath)
146 |
147 | //then
148 | exit shouldBe 0
149 | err shouldBe ""
150 | out shouldBe """http://scala-lang.org/resources/img/scala-logo-white.png
151 | |""".stripMargin
152 | }
153 |
154 | "countClassFiles" should "count .class files in directory and all sub-directories" in {
155 | //given
156 | val dir = "target/scala-2.11/classes/com"
157 |
158 | //when
159 | val count = countClassFiles(dir)
160 |
161 | //then
162 | count shouldBe 16
163 | }
164 |
165 | "Person" should "be serializable" in {
166 | //given
167 | val file = File.createTempFile("personSerialization", "bin")
168 | file.deleteOnExit()
169 | val fred = new Person("Fred")
170 | val bob = new Person("Bob")
171 | val john = new Person("John")
172 | fred.addFriend(bob)
173 | fred.addFriend(john)
174 | bob.addFriend(fred)
175 | bob.addFriend(john)
176 | john.addFriend(fred)
177 | john.addFriend(bob)
178 | val friends = Array[Person](fred, bob, john, new Person("Dummy"))
179 |
180 | //when
181 | savePersons(file, friends)
182 | val result: Array[Person] = readPersons(file)
183 |
184 | //then
185 | result.length shouldBe friends.length
186 | for ((person, friend) <- result.zip(friends)) {
187 | person.size shouldBe friend.size
188 | for ((p, f) <- person.zip(friend)) {
189 | p.name shouldBe f.name
190 | }
191 | }
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/src/test/scala/Chapter10Spec.scala:
--------------------------------------------------------------------------------
1 | import Chapter10._
2 | import java.beans.{PropertyChangeEvent, PropertyChangeListener}
3 | import java.io.{FilterInputStream, InputStream}
4 | import org.scalatest.{FlatSpec, Matchers}
5 | import scala.annotation.tailrec
6 | import scala.collection.mutable.ArrayBuffer
7 | import scala.io.Source
8 |
9 | class Chapter10Spec extends FlatSpec with Matchers {
10 |
11 | "RectangleLike" should "provide translate and grow methods" in {
12 | //given
13 | val egg = new java.awt.geom.Ellipse2D.Double(5, 10, 20, 30) with RectangleLike
14 |
15 | //when & then
16 | egg.translate(10, -10)
17 | egg.getX shouldBe 15
18 | egg.getY shouldBe 0
19 | egg.getWidth shouldBe 20
20 | egg.getHeight shouldBe 30
21 |
22 | //when & then
23 | egg.grow(10, 20)
24 | egg.getX shouldBe 5
25 | egg.getY shouldBe -20
26 | egg.getWidth shouldBe 40
27 | egg.getHeight shouldBe 70
28 | }
29 |
30 | "OrderedPoint" should "use lexicographic ordering" in {
31 | //when & then
32 | new OrderedPoint(1, 1).compare(new OrderedPoint(2, 2)) shouldBe -1
33 | new OrderedPoint(1, 1).compare(new OrderedPoint(2, 1)) shouldBe -1
34 | new OrderedPoint(1, 1).compare(new OrderedPoint(1, 2)) shouldBe -1
35 | new OrderedPoint(1, 1).compare(new OrderedPoint(1, 1)) shouldBe 0
36 | new OrderedPoint(1, 1).compare(new OrderedPoint(0, 1)) shouldBe 1
37 | new OrderedPoint(1, 1).compare(new OrderedPoint(1, 0)) shouldBe 1
38 | new OrderedPoint(1, 1).compare(new OrderedPoint(0, 0)) shouldBe 1
39 | }
40 |
41 | "bitSetLinearization" should "return linearization of BitSet traits" in {
42 | //when
43 | val result: List[String] = bitSetLinearization
44 |
45 | //then
46 | // got from here:
47 | // http://stackoverflow.com/questions/15623498/handy-ways-to-show-linearization-of-a-class
48 | val tpe = scala.reflect.runtime.universe.typeOf[scala.collection.BitSet]
49 | result shouldBe tpe.baseClasses.map(_.fullName)
50 | }
51 |
52 | "CryptoLogger" should "encrypt messages with the Caesar cipher default key" in {
53 | //given
54 | val logger = new TestLogger with CryptoLogger
55 |
56 | //when
57 | logger.log("12345")
58 |
59 | //then
60 | logger.message shouldBe "45678"
61 | }
62 |
63 | it should "encrypt messages with the Caesar cipher key -3" in {
64 | //given
65 | val logger = new TestLogger with CryptoLogger {
66 | override val key = -3
67 | }
68 |
69 | //when
70 | logger.log("45678")
71 |
72 | //then
73 | logger.message shouldBe "12345"
74 | }
75 |
76 | "PointBean" should "extend java.awt.Point with PropertyChangeSupportLike" in {
77 | //given
78 | val point = new PointBean
79 |
80 | //when
81 | var event: PropertyChangeEvent = null
82 | point.addPropertyChangeListener("x", new PropertyChangeListener {
83 | override def propertyChange(evt: PropertyChangeEvent): Unit = {
84 | event = evt
85 | }
86 | })
87 | point.x += 1
88 | point.firePropertyChange("x", 0, 1)
89 |
90 | //then
91 | point.hasListeners("x") shouldBe true
92 | point.hasListeners("y") shouldBe false
93 | event.getPropertyName shouldBe "x"
94 | event.getOldValue shouldBe 0
95 | event.getNewValue shouldBe 1
96 | }
97 |
98 | "TestEngine" should "extend Engine with InfiniteEngine" in {
99 | //given
100 | val engine = new TestEngine
101 |
102 | //when
103 | engine.start()
104 | engine.stop()
105 |
106 | //then
107 | engine.model shouldBe "test"
108 | }
109 |
110 | "BufferedInputStreamLike" should "add buffering to an input stream" in {
111 | //given
112 | val in = new {
113 | override val bufferSize = 48
114 | } with FilterInputStream(getClass.getResourceAsStream("/myfile.txt"))
115 | with BufferedInputStreamLike
116 | with ConsoleLogger
117 |
118 | try {
119 | //when
120 | val result = Source.fromBytes(readBytes(in)).mkString
121 |
122 | //then
123 | result shouldBe """
124 | |Simple text file with example words.
125 | |We will parse the file and count the words.
126 | |""".stripMargin
127 | }
128 | finally {
129 | in.close()
130 | }
131 | }
132 |
133 | "IterableInputStream" should "extends java.io.InputStream with the trait Iterable[Byte]" in {
134 | //given
135 | val in = new IterableInputStream(getClass.getResourceAsStream("/myfile.txt"))
136 |
137 | try {
138 | //when
139 | val result = new ArrayBuffer[Byte]
140 | result ++= in
141 |
142 | //then
143 | new String(result.toArray) shouldBe """
144 | |Simple text file with example words.
145 | |We will parse the file and count the words.
146 | |""".stripMargin
147 | }
148 | finally {
149 | in.close()
150 | }
151 | }
152 |
153 | private def readBytes(in: InputStream): Array[Byte] = {
154 | val buf = new ArrayBuffer[Byte]
155 |
156 | @tailrec
157 | def read(): Unit = {
158 | val byte = in.read()
159 | if (byte == -1) return
160 |
161 | buf += byte.toByte
162 | read()
163 | }
164 |
165 | read()
166 | buf.toArray
167 | }
168 |
169 | class TestLogger extends Logger {
170 | var message = ""
171 | override def log(msg: String): Unit = message = msg
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/src/test/scala/Chapter11Spec.scala:
--------------------------------------------------------------------------------
1 | import Chapter11._
2 | import org.scalatest.{FlatSpec, Matchers}
3 |
4 | class Chapter11Spec extends FlatSpec with Matchers {
5 |
6 | "Task1" should "evaluate expressions according to the precedence rules" in {
7 | //when & then
8 | (3 + 4 -> 5) shouldBe ((3 + 4) -> 5)
9 | (3 -> 4 + "5") shouldBe ((3 -> 4) + "5")
10 | }
11 |
12 | "Fraction" should "be normalized and has operations +, -, *, /" in {
13 | //when & then
14 | Fraction(15, -6).toString shouldBe "-5/2"
15 | (Fraction(1, 2) + Fraction(1, 2)).toString shouldBe "1/1"
16 | (Fraction(1, 2) - Fraction(1, 2)).toString shouldBe "0/1"
17 | (Fraction(1, 2) * Fraction(1, 2)).toString shouldBe "1/4"
18 | (Fraction(1, 2) / Fraction(1, 2)).toString shouldBe "1/1"
19 | (Fraction(1, 7) - Fraction(2, 9)).toString shouldBe "-5/63"
20 |
21 | //equals
22 | Fraction(15, -6) == Fraction(15, -6) shouldBe true
23 | Fraction(15, -6) == Fraction(-15, 6) shouldBe true
24 | Fraction(15, 6) == Fraction(-15, 6) shouldBe false
25 | //noinspection ComparingUnrelatedTypes
26 | Fraction(15, 6).equals("") shouldBe false
27 | Fraction(15, 6) == null shouldBe false
28 | }
29 |
30 | "Money" should "be normalized and has operations +, -, ==, <" in {
31 | //when & then
32 | Money(1, 150).toString shouldBe "2.50"
33 | (Money(1, 75) + Money(0, 50)).toString shouldBe "2.25"
34 | (Money(1, 75) + Money(0, 10)).toString shouldBe "1.85"
35 | (Money(2, 25) - Money(0, 50)).toString shouldBe "1.75"
36 | (Money(2, 25) - Money(0, 25)).toString shouldBe "2.00"
37 | Money(2, 25) == Money(0, 50) shouldBe false
38 | Money(2, 25) == Money(2, 25) shouldBe true
39 | Money(2, 25) < Money(2, 25) shouldBe false
40 | Money(1, 50) < Money(2, 25) shouldBe true
41 | }
42 |
43 | "Table" should "provide operators that construct an HTML table" in {
44 | //when
45 | val table = Table() | "Java" | "Scala" || "Gosling" | "Odersky" || "JVM" | "JVM, .NET"
46 |
47 | //then
48 | table.toHtml shouldBe "
"
51 | }
52 |
53 | "ASCIIArt" should "provide operators for combining figures horizontally and vertically" in {
54 | //given
55 | val art = new ASCIIArt +
56 | """ /\_/\""" +
57 | """( ' ' )""" +
58 | """( - )""" +
59 | """ | | |""" +
60 | """(__|__)"""
61 |
62 | //when
63 | val result = art | new ASCIIArt +
64 | """ -----""" +
65 | """ / Hello \""" +
66 | """ < Scala |""" +
67 | """ \ Coder /""" +
68 | """ -----"""
69 |
70 | //then
71 | art.toString shouldBe """ /\_/\
72 | |( ' ' )
73 | |( - )
74 | | | | |
75 | |(__|__)""".stripMargin
76 |
77 | result.toString shouldBe """ /\_/\ -----
78 | |( ' ' ) / Hello \
79 | |( - ) < Scala |
80 | | | | | \ Coder /
81 | |(__|__) -----""".stripMargin
82 | }
83 |
84 | "BitSequence" should "store 64 bits, packed in Long" in {
85 | //given
86 | val bits = new BitSequence
87 |
88 | //when & then
89 | bits(0) shouldBe 0
90 | bits(0) = 1
91 | bits(0) shouldBe 1
92 | bits(1) shouldBe 0
93 | bits(1) = 1
94 | bits(1) shouldBe 1
95 | bits(63) shouldBe 0
96 | bits(63) = 1
97 | bits(63) shouldBe 1
98 | bits(0) shouldBe 1
99 | bits(0) = 0
100 | bits(0) shouldBe 0
101 | bits(1) shouldBe 1
102 | bits(1) = 0
103 | bits(1) shouldBe 0
104 | bits(63) shouldBe 1
105 | bits(63) = 0
106 | bits(63) shouldBe 0
107 | }
108 |
109 | "Matrix" should "supply operator + and apply method" in {
110 | //when
111 | val result = Matrix(2, 2)(
112 | 1, 2,
113 | 3, 4) +
114 | Matrix(2, 2)(
115 | 5, 6,
116 | 7, 8)
117 |
118 | //then
119 | result(0, 0) shouldBe 6
120 | result(0, 1) shouldBe 8
121 | result.toString shouldBe """[6, 8]
122 | |[10, 12]""".stripMargin
123 | }
124 |
125 | it should "supply operator * for scalars" in {
126 | //when
127 | val result = Matrix(2, 2)(
128 | 1, 2,
129 | 3, 4) * 2
130 |
131 | //then
132 | result.toString shouldBe """[2, 4]
133 | |[6, 8]""".stripMargin
134 | }
135 |
136 | it should "supply operator *" in {
137 | //when
138 | val result = Matrix(3, 2)(
139 | 1, 2,
140 | 3, 4,
141 | 5, 6) *
142 | Matrix(2, 4)(
143 | 1, 2, 3, 4,
144 | 5, 6, 7, 8)
145 |
146 | //then
147 | result.toString shouldBe """[11, 14, 17, 20]
148 | |[23, 30, 37, 44]
149 | |[35, 46, 57, 68]""".stripMargin
150 | }
151 |
152 | it should "be error tolerant" in {
153 | //when & then
154 | a [IllegalArgumentException] should be thrownBy {
155 | Matrix(1, 0)(1)
156 | }
157 | a [IllegalArgumentException] should be thrownBy {
158 | Matrix(1, 1)(1) + Matrix(1, 2)(1, 1)
159 | }
160 | a [IllegalArgumentException] should be thrownBy {
161 | Matrix(1, 1)(1) * Matrix(2, 1)(1, 1)
162 | }
163 | }
164 |
165 | "RichFile" should "extract the file path, name, and extension" in {
166 | //given
167 | val file = "file/home/cay/readme.txt"
168 |
169 | //when
170 | val RichFile(path, name, extension) = file
171 |
172 | //then
173 | path shouldBe "/home/cay"
174 | name shouldBe "readme"
175 | extension shouldBe "txt"
176 | }
177 |
178 | "RichFile2" should "extract the path sequence" in {
179 | //given
180 | val file = "file/home/cay/data/readme.txt"
181 |
182 | //when
183 | val RichFile2(home, user, data, name) = file
184 |
185 | //then
186 | home shouldBe "home"
187 | user shouldBe "cay"
188 | data shouldBe "data"
189 | name shouldBe "readme.txt"
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/src/test/scala/Chapter12Spec.scala:
--------------------------------------------------------------------------------
1 | import Chapter12._
2 | import org.scalatest.{FlatSpec, Matchers}
3 |
4 | class Chapter12Spec extends FlatSpec with Matchers {
5 |
6 | "values" should "yield a collection of function inputs and outputs in a given range" in {
7 | //when
8 | val result: Seq[(Int, Int)] = values(x => x * x, -5, 5)
9 |
10 | //then
11 | result shouldBe Seq((-5, 25), (-4, 16), (-3, 9), (-2, 4), (-1, 1), (0, 0),
12 | (1, 1), (2, 4), (3, 9), (4, 16), (5, 25))
13 | }
14 |
15 | "largestElement" should "return the largest element of an array with reduceLeft" in {
16 | //given
17 | val arr = Array(5, 6, 1, 0, -2, 3, -1)
18 |
19 | //when
20 | val result: Int = largestElement(arr)
21 |
22 | //then
23 | result shouldBe 6
24 | }
25 |
26 | "factorial" should "be implemented using to and reduceLeft" in {
27 | //when & then
28 | factorial(-1) shouldBe 1
29 | factorial(0) shouldBe 1
30 | factorial(1) shouldBe 1
31 | factorial(5) shouldBe 120
32 | }
33 |
34 | "factorial2" should "be implemented using to and foldLeft" in {
35 | //when & then
36 | factorial2(-1) shouldBe 1
37 | factorial2(0) shouldBe 1
38 | factorial2(1) shouldBe 1
39 | factorial2(5) shouldBe 120
40 | }
41 |
42 | "largest" should "return the largest value of a function within a given sequence of inputs" in {
43 | //when
44 | val result: Int = largest(x => 10 * x - x * x, 1 to 10)
45 |
46 | //then
47 | result shouldBe 25
48 | }
49 |
50 | "largestAt" should "return the input at which the output is largest" in {
51 | //when
52 | val result: Int = largestAt(x => 10 * x - x * x, 1 to 10)
53 |
54 | //then
55 | result shouldBe 5
56 | }
57 |
58 | "adjustToPair" should "return the function that operates on a pair" in {
59 | //when & then
60 | adjustToPair(_ * _)((6, 7)) shouldBe 42
61 | }
62 |
63 | "mapPairs" should "compute the sums of the elements in pairs" in {
64 | //given
65 | val pairs = (1 to 10) zip (11 to 20)
66 |
67 | //when
68 | val result: Seq[Int] = mapPairs(pairs, _ + _)
69 |
70 | result shouldBe Seq(12, 14, 16, 18, 20, 22, 24, 26, 28, 30)
71 | }
72 |
73 | "correspondsLen" should "check that strings have the lengths from integers array" in {
74 | //when & then
75 | correspondsLen(Array(""), Array(0)) shouldBe true
76 | correspondsLen(Array(""), Array(1)) shouldBe false
77 | correspondsLen(Array("a"), Array(1)) shouldBe true
78 | correspondsLen(Array("a"), Array(2)) shouldBe false
79 | correspondsLen(Array("a", "bb", "ccc"), Array(1, 2, 3)) shouldBe true
80 | correspondsLen(Array("a", "bb", "ccc"), Array(1, 2, 3, 4)) shouldBe false
81 | correspondsLen(Array("a", "bb", "ccc"), Array(1, 2)) shouldBe false
82 | }
83 |
84 | "corresponds2" should "be implemented without currying" in {
85 | corresponds2(Array("a"), Array(1), (a: String, b: Int) => a.length == b) shouldBe true
86 | }
87 |
88 | "unless" should "work just like if, but with an inverted condition" in {
89 | //when & then
90 | var a = 0
91 | unless(a == 1) {
92 | a = 1
93 | }
94 |
95 | a shouldBe 1
96 |
97 | var b = 0
98 | unless(b == 0) {
99 | b = 1
100 | }
101 |
102 | b shouldBe 0
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/test/scala/Chapter13Spec.scala:
--------------------------------------------------------------------------------
1 | import Chapter13._
2 | import org.scalatest.{FlatSpec, Matchers}
3 | import scala.collection.mutable
4 | import scala.io.Source
5 |
6 | class Chapter13Spec extends FlatSpec with Matchers {
7 |
8 | "indexes" should "produce a mutable map of mutable set of indexes of all characters" in {
9 | //when
10 | val result: mutable.Map[Char, mutable.Set[Int]] = indexes("Mississippi")
11 |
12 | //then
13 | result('M') shouldBe mutable.Set(0)
14 | result('i') shouldBe mutable.Set(1, 4, 7, 10)
15 | result('s') shouldBe mutable.Set(2, 3, 5, 6)
16 | result('p') shouldBe mutable.Set(8, 9)
17 | }
18 |
19 | "indexes2" should "produce an immutable map of lists of indexes of all characters" in {
20 | //when
21 | val result: Map[Char, List[Int]] = indexes2("Mississippi")
22 |
23 | //then
24 | result('M') shouldBe List(0)
25 | result('i') shouldBe List(1, 4, 7, 10)
26 | result('s') shouldBe List(2, 3, 5, 6)
27 | result('p') shouldBe List(8, 9)
28 | }
29 |
30 | "removeAllZeroes" should "remove all zeroes from a linked list of integers" in {
31 | //when & then
32 | val list: LinkedList[Int] = newLinkedList(1, 2, 3)
33 | val result: LinkedList[Int] = removeAllZeroes(list)
34 | result shouldBe theSameInstanceAs(list)
35 |
36 | removeAllZeroes(newLinkedList(0, 1, 2, 3, 4)) shouldBe newLinkedList(1, 2, 3, 4)
37 | removeAllZeroes(newLinkedList(1, 2, 0, 3, 4)) shouldBe newLinkedList(1, 2, 3, 4)
38 | removeAllZeroes(newLinkedList(1, 2, 3, 4, 0)) shouldBe newLinkedList(1, 2, 3, 4)
39 | removeAllZeroes(newLinkedList(0, 1, 2, 0, 3, 4, 0)) shouldBe newLinkedList(1, 2, 3, 4)
40 | removeAllZeroes(newLinkedList(0, 0, 1, 2, 0, 0, 3, 0, 0, 4, 0, 0)) shouldBe
41 | newLinkedList(1, 2, 3, 4)
42 | }
43 |
44 | private def newLinkedList(elems: Int*): LinkedList[Int] = mutable.LinkedList(elems: _*)
45 |
46 | "mapToValues" should "return collection of corresponding integer values from map" in {
47 | //given
48 | val coll = Array("Tom", "Fred", "Harry")
49 | val map = Map("Tom" -> 3, "Dick" -> 4, "Harry" -> 5)
50 |
51 | //when
52 | val result = mapToValues(coll, map)
53 |
54 | //then
55 | result shouldBe Array(3, 5)
56 | }
57 |
58 | "collToString" should "works just like mkString, using reduceLeft" in {
59 | //when & then
60 | collToString(Nil) shouldBe ""
61 | collToString(Array(1)) shouldBe "1"
62 | collToString(Seq(1, 2, 3)) shouldBe "1, 2, 3"
63 | collToString(List("1", "2", "3")) shouldBe "1, 2, 3"
64 | }
65 |
66 | "reversList" should "revers the given list" in {
67 | //given
68 | val lst = List(1, 2, 3, 4, 5)
69 |
70 | //when
71 | val result: List[Int] = reversList(lst)
72 |
73 | //then
74 | result shouldBe List(5, 4, 3, 2, 1)
75 | }
76 |
77 | "multiply" should "apply Function.tupled to the multiplication function" in {
78 | //given
79 | val prices = List(1, 2, 3)
80 | val quantities = List(10, 20, 30)
81 |
82 | //when
83 | val result = multiply(prices, quantities)
84 |
85 | //then
86 | result shouldBe List(10, 40, 90)
87 | }
88 |
89 | "twoDimensionalArray" should "turn an array of Double values into a two-dimensional array" in {
90 | //given
91 | val arr = Array[Double](1, 2, 3, 4, 5, 6)
92 | val columns = 3
93 |
94 | //when
95 | val result: Array[Array[Double]] = twoDimensionalArray(arr, columns)
96 |
97 | //then
98 | result shouldBe Array[Array[Double]](Array(1, 2, 3), Array(4, 5, 6))
99 | }
100 |
101 | "getLetterFrequencyMap" should "return letter frequency map for the given files" in {
102 | //given
103 | val files = Array.fill(5)("/myfile.txt")
104 |
105 | //when
106 | val result: Map[Char, Int] = getLetterFrequencyMap(files)
107 |
108 | //then
109 | result shouldBe getTestFrequencyMap(files.head, files.length)
110 | }
111 |
112 | "getLetterFrequencyMap2" should "return letter frequency map for the given string" in {
113 | //given
114 | val str = Source.fromInputStream(getClass.getResourceAsStream("/myfile.txt")).mkString
115 |
116 | //when
117 | val result: Map[Char, Int] = getLetterFrequencyMap2(str)
118 |
119 | //then
120 | result shouldBe getTestFrequencyMap("/myfile.txt", 1)
121 | }
122 |
123 | private def getTestFrequencyMap(file: String, multiplier: Int): Map[Char, Int] = {
124 | val result = new mutable.HashMap[Char, Int]()
125 | for (c <- Source.fromInputStream(getClass.getResourceAsStream(file))) {
126 | result(c) = result.getOrElse(c, 0) + 1
127 | }
128 |
129 | for ((k, v) <- result) {
130 | result(k) = v * multiplier
131 | }
132 |
133 | result.toMap
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/src/test/scala/Chapter14Spec.scala:
--------------------------------------------------------------------------------
1 | import Chapter14._
2 | import org.scalatest.{FlatSpec, Matchers}
3 |
4 | class Chapter14Spec extends FlatSpec with Matchers {
5 |
6 | "swap" should "returns the pair with the components swapped" in {
7 | //given
8 | val pair = (1, 2)
9 |
10 | //when
11 | val result: (Int, Int) = swap(pair)
12 |
13 | //then
14 | result shouldBe (2, 1)
15 | }
16 |
17 | "swap2" should "swap the first two elements of an array" in {
18 | //when & then
19 | var arr: Array[Int] = Array()
20 | val r1: Array[Int] = swap2(arr)
21 | r1 shouldBe theSameInstanceAs(arr)
22 | r1 shouldBe Array()
23 |
24 | arr = Array(1)
25 | val r2 = swap2(arr)
26 | r2 shouldBe theSameInstanceAs(arr)
27 | r2 shouldBe Array(1)
28 |
29 | arr = Array(1, 2)
30 | val r3 = swap2(arr)
31 | r3 shouldBe theSameInstanceAs(arr)
32 | r3 shouldBe Array(2, 1)
33 |
34 | arr = Array(1, 2, 3, 4)
35 | val r4 = swap2(arr)
36 | r4 shouldBe theSameInstanceAs(arr)
37 | r4 shouldBe Array(2, 1, 3, 4)
38 | }
39 |
40 | "price" should "handle Multiple class items" in {
41 | //given
42 | val item = Bundle("Father's day special", 20.0,
43 | Article("Scala for the Impatient", 39.95),
44 | Bundle("Anchor Distilley Sampler", 10.0,
45 | Article("Old Potrero Straight Rye Whiskey", 79.95),
46 | Article("Junipero Gin", 32.95)),
47 | Multiple(10, Article("Blackwell Toaster", 29.95)))
48 |
49 | //when
50 | val result: Double = price(item)
51 |
52 | //then
53 | result.formatted("%.2f") shouldBe "422.35"
54 | }
55 |
56 | it should "be able to handle any items, such as bundles or multiples" in {
57 | //when & then
58 | price(Multiple(1,
59 | Multiple(2,
60 | Bundle("Black Friday special", 49.0,
61 | Multiple(3, Article("iPhone 5s", 549.99)))))
62 | ).formatted("%.2f") shouldBe "3201.94"
63 | }
64 |
65 | "leafSum" should "compute the sum of all elements in the leaves" in {
66 | //given
67 | val list: List[Any] = List(List(3, 8), 2, List(5))
68 |
69 | //when
70 | val result: Double = leafSum(list)
71 |
72 | //then
73 | result shouldBe 18
74 | }
75 |
76 | "Task6.leafSum" should "compute the sum of all elements in the BinaryTree" in {
77 | import Task6._
78 |
79 | //given
80 | val bt: Task6.BinaryTree = Node(Node(Leaf(3), Leaf(8)), Node(Leaf(2), Leaf(5)))
81 |
82 | //when
83 | val result: Int = leafSum(bt)
84 |
85 | //then
86 | result shouldBe 18
87 | }
88 |
89 | "Task7.leafSum" should "compute the sum of all elements in the multi-node tree" in {
90 | import Task7._
91 |
92 | //given
93 | val bt: Task7.BinaryTree = Node(Node(Leaf(3), Leaf(8)), Leaf(2), Node(Leaf(5)))
94 |
95 | //when
96 | val result: Int = leafSum(bt)
97 |
98 | //then
99 | result shouldBe 18
100 | }
101 |
102 | "eval" should "compute the value of the operator-node tree" in {
103 | import Task8._
104 | import Task8.Op._
105 |
106 | //given
107 | val bt: Task8.BinaryTree =
108 | Node(Plus, Node(Product, Leaf(3), Leaf(8)), Leaf(2), Node(Minus, Leaf(5)))
109 |
110 | //when
111 | val result: Int = eval(bt)
112 |
113 | //then
114 | result shouldBe 21
115 | }
116 |
117 | "sumOfNonNoneValues" should "compute the sum of the non-None values" in {
118 | //given
119 | val list: List[Option[Int]] = List(None, Some(1), None, Some(2), None, Some(3))
120 |
121 | //when
122 | val result: Int = sumOfNonNoneValues(list)
123 |
124 | //then
125 | result shouldBe 6
126 | }
127 |
128 | "compose" should "yield None if either function does" in {
129 | //given
130 | def f(x: Double) = if (x >= 0) Some(Math.sqrt(x)) else None
131 | def g(x: Double) = if (x != 1) Some(1 / (x - 1)) else None
132 |
133 | //when
134 | val h = compose(f, g)
135 |
136 | //then
137 | h(2) shouldBe Some(1)
138 | h(1) shouldBe None
139 | h(0) shouldBe None
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/src/test/scala/Chapter15Spec.scala:
--------------------------------------------------------------------------------
1 | import Chapter15._
2 | import Chapter15Task4.scalaSum
3 | import Chapter15Task5.scalaFileToString
4 | import TestUtils.{ClassPath, runApp, runCmd}
5 | import java.io.IOException
6 | import org.scalatest.{FlatSpec, Matchers}
7 |
8 | class Chapter15Spec extends FlatSpec with Matchers {
9 |
10 | "Task1" should "has four test cases that use the org.junit.Test annotation" in {
11 | //when
12 | val (exit, out, err) = runApp("org.junit.runner.JUnitCore", "Chapter15$Task1")
13 |
14 | //then
15 | exit shouldBe 0
16 | err shouldBe ""
17 | out should include ("OK (4 tests)")
18 | }
19 |
20 | "scalaSum" should "call Scala sum method with variable arguments from Java" in {
21 | //when & then
22 | scalaSum(1, 2, 3) shouldBe 6
23 | sum(1, 2, 3, 4) shouldBe 10
24 | }
25 |
26 | "scalaFileToString" should "call Scala fileToString method" in {
27 | //when & then
28 | scalaFileToString("/myfile.txt") shouldBe """
29 | |Simple text file with example words.
30 | |We will parse the file and count the words.
31 | |""".stripMargin
32 | a [RuntimeException] should be thrownBy {
33 | scalaFileToString("nonExisting.file")
34 | }
35 | a [IOException] should be thrownBy {
36 | fileToString("nonExisting.file")
37 | }
38 | }
39 |
40 | "Work" should "emulate work with two threads and one shared volatile boolean flag" in {
41 | //when
42 | val (exit, out, err) = runApp("Chapter15WorkApp")
43 |
44 | //then
45 | exit shouldBe 0
46 | err shouldBe ""
47 | out should include ("Work.done flag was set to true")
48 | out should include ("Work is done, exiting")
49 | }
50 |
51 | "TailRecursion" should "provide non-overridden, tail-recursive method" in {
52 | //given
53 | val tr = new TailRecursion
54 |
55 | //when
56 | val result: Int = tr.sum(List(1, 2, 3, 4))
57 |
58 | //then
59 | result shouldBe 10
60 | }
61 |
62 | "allDifferent" should "be generated for all primitive types" in {
63 | //when
64 | val (exit, out, err) = runCmd("javap", "-classpath", ClassPath, "Chapter15$")
65 |
66 | //then
67 | exit shouldBe 0
68 | err shouldBe ""
69 | out should include ("public " +
49 | "Java Scala " +
50 | "Gosling Odersky JVM JVM, .NET
21 |
26 |
27 | result.toString() shouldBe """
28 | |
""".stripMargin
33 | }
34 |
35 | "task3" should "check expressions" in {
36 | //when & then
37 | val res1 =
59 | |
60 | |""".stripMargin
61 | }
62 |
63 | "printAllImg" should "print all img src attributes" in {
64 | //given
65 | val file = "/Chapter16Task04.html"
66 |
67 | //when
68 | val (exit, out, err) = runApp("Chapter16PrintAllImgApp", file)
69 |
70 | //then
71 | exit shouldBe 0
72 | err shouldBe ""
73 | out shouldBe """http://test.com/img1.png
74 | |http://test.com/img2.png
75 | |http://test.com/img3.png
76 | |""".stripMargin
77 | }
78 |
79 | "printAllHyperlinks" should "print a table of all hyperlinks together with their URLs" in {
80 | //given
81 | val file = "/Chapter16Task04.html"
82 |
83 | //when
84 | val (exit, out, err) = runApp("Chapter16PrintAllHyperlinksApp", file)
85 |
86 | //then
87 | exit shouldBe 0
88 | err shouldBe ""
89 | out shouldBe """+---------------------------------------+----------------------+
90 | ||
| http://test.com/ref1 |
91 | || Ref 2 | http://test.com/ref2 |
92 | || Ref 3 | http://test.com/ref3 |
93 | |+---------------------------------------+----------------------+
94 | |""".stripMargin
95 | }
96 |
97 | "mapToXml" should "produce XML from the given map" in {
98 | //given
99 | val map = Map("A" -> "1", "B" -> "2")
100 |
101 | //when
102 | val result: Elem = mapToXml(map)
103 |
104 | //then
105 | result shouldBe
106 | }
107 |
108 | "xmlToMap" should "produce map from the given Xml" in {
109 | //given
110 | val xml =
111 |
112 | //when
113 | val result: Map[String, String] = xmlToMap(xml)
114 |
115 | //then
116 | result shouldBe Map("A" -> "1", "B" -> "2")
117 | }
118 |
119 | "transformXhtml" should "transform an XHTML document" in {
120 | //given
121 | val root = XML.load(getClass.getResourceAsStream("/Chapter16Task04.html"))
122 |
123 | //when
124 | val result: Node = transformXhtml(root)
125 |
126 | //then
127 | result.toString() shouldBe """
128 | |
129 | |
132 | |
133 | |
137 | |
138 | | Ref 3
139 | |
141 | |
142 | |""".stripMargin
143 | }
144 |
145 | "transformXhtmlFile" should "transform XHTML file and saves the result to another file" in {
146 | //given
147 | val inFile = "/Chapter16Task10.xhtml"
148 | val tmpFile = File.createTempFile("Chapter16Task10Out", "xhtml")
149 | tmpFile.deleteOnExit()
150 |
151 | //when
152 | transformXhtmlFile(inFile, tmpFile.getAbsolutePath)
153 |
154 | //then
155 | Source.fromFile(tmpFile).mkString shouldBe
156 | """
157 | |
158 | |
159 | |
170 | |
171 | |
177 | |
178 | | Ref 3
179 | |
182 | |