-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy path03_Control_Structures.sc
765 lines (666 loc) · 18.7 KB
/
03_Control_Structures.sc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
val x = if (a) y else z
for (line <- source.getLines) {
for {
char <- line
if char.isLetter
} println(char)
// char algorithm here ...
}
for {
line <- source.getLines
char <- line
if char.isLetter
} yield char
// char algorithm here ...
val nieces = List("emily", "hannah", "mercedes", "porsche")
for (n <- nieces) yield n.capitalize
// 3.1 Looping with for and foreach
val a = Array("apple", "banana", "orange")
for (e <- a) println(e)
for (e <- a) {
// imagine this requires multiple lines
val s = e.toUpperCase
println(s)
}
val newArray = for (e <- a) yield e.toUpperCase
val newArray = for (e <- a) yield {
// imagine this requires multiple lines
val s = e.toUpperCase
s
}
for (i <- 0 until a.length) {
println(s"$i is ${a(i)}")
}
for ((e, count) <- a.zipWithIndex) {
println(s"$count is $e")
}
for (i <- 1 to 3) println(i)
1 to 3
for (i <- 1 to 10 if i < 4) println(i)
val names = Map("fname" -> "Robert", "lname" -> "Goren")
for ((k, v) <- names) println(s"key: $k, value: $v")
a.foreach(println)
a.foreach(e => println(e.toUpperCase))
a.foreach { e =>
val s = e.toUpperCase
println(s)
}
// $ scalac -Xprint:parse Main.scala
class Main {
for (i <- 1 to 10) println(i)
}
// $ scalac -Xprint:parse Main.scala
// using ammonite
/*
desugar(for (i <- 1 to 10) println(i))
res133: Desugared = scala.Predef.intWrapper(1).to(10).foreach[Unit](((i: Int) => scala.Predef.println(i)))
*/
val nums = List(1, 2, 3)
for (i <- nums) println(i)
nums.foreach(println)
for (i <- 1 to 10) println(i)
1.to(10).foreach(println)
1 to 10 foreach println
for {
i <- 1 to 10
if i % 2 == 0
} println(i)
1.to(10).withFilter(_ % 2 == 0).foreach(println)
for {
i <- 1 to 10
if i != 1
if i % 2 == 0
} println(i)
1.to(10).withFilter(_ != 1).withFilter(_ % 2 == 0).foreach(println)
for { i <- 1 to 10 } yield i
1.to(10).map(identity)
// 3.2 Using for Loops with Multiple Counters
for (i <- 1 to 2; j <- 1 to 2) println(s"i = $i, j = $j")
for {
i <- 1 to 2
j <- 1 to 2
} println(s"i = $i, j = $j")
for {
i <- 1 to 3
j <- 1 to 5
k <- 1 to 10
} println(s"i = $i, j = $j, k = $k")
val array = Array.ofDim[Int](2, 2)
array(0)(0) = 0
array(0)(1) = 1
array(1)(0) = 2
array(1)(1) = 3
for {
i <- 0 to 1
j <- 0 to 1
} println(s"($i)($j) = ${array(i)(j)}")
for {
i <- 1 to 2
j <- 2 to 3
} println(s"i = $i, j = $j")
// 3.3 Using a for Loop with Embedded if Statements (Guards)
for (i <- 1 to 10 if i % 2 == 0) println(i)
for {
i <- 1 to 10
if i % 2 == 0
} println(i)
for {
i <- 1 to 10
if i > 3
if i < 6
if i % 2 == 0
} println(i)
for (file <- files) {
if (hasSoundFileExtension(file) && !soundFileIsLong(file)) {
soundFiles += file
}
}
for {
file <- files
if passesFilter1(file)
if passesFilter2(file)
} doSomething(file)
// 3.4 Creating a for Comprehension (for/yield Combination)
val names = Array("chris", "ed", "maurice")
val capNames = for (e <- names) yield e.capitalize
val lengths = for (e <- names) yield {
// imagine that this required multiple lines of code
e.length
}
var fruits = scala.collection.mutable.ArrayBuffer[String]()
fruits += "apple"
fruits += "banana"
fruits += "orange"
val out = for (e <- fruits) yield e.toUpperCase // an ArrayBuffer
val fruits = "apple" :: "banana" :: "orange" :: Nil
val out = for (e <- fruits) yield e.toUpperCase // a List
val out = for (e <- fruits) yield e.toUpperCase
val out = fruits.map(_.toUpperCase)
// 3.5 Implementing break and continue
// package com.alvinalexander.breakandcontinue
import scala.util.control.Breaks._
object BreakAndContinueDemo extends App {
println("\n=== BREAK EXAMPLE ===")
breakable {
for (i <- 1 to 10) {
println(i)
if (i > 4) break // break out of the for loop
}
}
println("\n=== CONTINUE EXAMPLE ===")
val searchMe = "peter piper picked a peck of pickled peppers"
var numPs = 0
for (i <- 0 until searchMe.length) {
breakable {
if (searchMe.charAt(i) != 'p') {
break // break out of the 'breakable', continue the outside loop
} else {
numPs += 1
}
}
}
println("Found " + numPs + " p's in the string.")
}
private val breakException = new BreakControl
def break(): Nothing = { throw breakException }
def breakable(op: => Unit) {
try {
op
} catch {
case ex: BreakControl =>
if (ex ne breakException) throw ex
}
}
// break
breakable {
for (x <- xs) {
if (cond)
break
}
}
// continue
for (x <- xs) {
breakable {
if (cond)
break
}
}
val count = searchMe.count(_ == 'p')
// package com.alvinalexander.labeledbreaks
object LabeledBreakDemo extends App {
import scala.util.control._
val Inner = new Breaks
val Outer = new Breaks
Outer.breakable {
for (i <- 1 to 5) {
Inner.breakable {
for (j <- 'a' to 'e') {
if (i == 1 && j == 'c')
Inner.break
else
println(s"i: $i, j: $j")
if (i == 2 && j == 'b')
Outer.break
}
}
}
}
}
import scala.util.control._
val Exit = new Breaks
Exit.breakable {
for (j <- 'a' to 'e') {
if (j == 'c')
Exit.break
else
println(s"j: $j")
}
}
var barrelIsFull = false
for (monkey <- monkeyCollection if !barrelIsFull) {
addMonkeyToBarrel(monkey)
barrelIsFull = checkIfBarrelIsFull
}
def sumToMax(arr: Array[Int], limit: Int): Int = {
var sum = 0
for (i <- arr) {
sum += i
if (sum > limit)
return limit
}
sum
}
val a = Array.range(0, 10)
println(sumToMax(a, 10))
import scala.annotation.tailrec
def factorial(n: Int): Int = {
@tailrec
def factorialAcc(acc: Int, n: Int): Int = {
if (n <= 1) acc
else factorialAcc(n * acc, n - 1)
}
factorialAcc(1, n)
}
// 3.6 Using the if Construct Like a Ternary Operator
// val absValue = if (a < 0) -a else a
if (i == 0) "a" else "b"
hash = hash * prime + (if (name == null) 0 else name.hashCode)
def abs(x: Int) = if (x >= 0) x else -x
def max(a: Int, b: Int) = if (a > b) a else b
val c = if (a > b) a else b
// 3.7 Using a Match Expression Like a switch Statement
// i is an integer
i match {
case 1 => println("January")
case 2 => println("February")
case 3 => println("March")
case 4 => println("April")
case 5 => println("May")
case 6 => println("June")
case 7 => println("July")
case 8 => println("August")
case 9 => println("September")
case 10 => println("October")
case 11 => println("November")
case 12 => println("December")
// catch the default with a variable so you can print it
case whoa => println("Unexpected case: " + whoa.toString)
}
val month = i match {
case 1 => "January"
case 2 => "February"
case 3 => "March"
case 4 => "April"
case 5 => "May"
case 6 => "June"
case 7 => "July"
case 8 => "August"
case 9 => "September"
case 10 => "October"
case 11 => "November"
case 12 => "December"
case _ => "Invalid month" // the default, catch-all
}
// The @switch annotation
// Version 1 - compiles to a tableswitch
import scala.annotation.switch
class SwitchDemo {
val i = 1
val x = (i: @switch) match {
case 1 => "One"
case 2 => "Two"
case _ => "Other"
}
}
import scala.annotation.switch
// Version 2 - leads to a compiler warning
class SwitchDemo {
val i = 1
val Two = 2 // added
val x = (i: @switch) match {
case 1 => "One"
case Two => "Two" // replaced the '2'
case _ => "Other"
}
}
def getClassAsString(x: Any): String = x match {
case s: String => s + " is a String"
case i: Int => "Int"
case f: Float => "Float"
case l: List[_] => "List"
case p: Person => "Person"
case _ => "Unknown"
}
// case _ => println("Got a default match")
// case default => println(default)
// case oops => println(oops)
i match {
case 0 => println("0 received")
case 1 => println("1 is good, too")
}
val monthNumberToName = Map(
1 -> "January",
2 -> "February",
3 -> "March",
4 -> "April",
5 -> "May",
6 -> "June",
7 -> "July",
8 -> "August",
9 -> "September",
10 -> "October",
11 -> "November",
12 -> "December"
)
val monthName = monthNumberToName(4)
println(monthName) // prints "April"
// 3.8 Matching Multiple Conditions with One Case Statement
val i = 5 i match {
case 1 | 3 | 5 | 7 | 9 => println("odd")
case 2 | 4 | 6 | 8 | 10 => println("even")
}
val cmd = "stop"
cmd match {
case "start" | "go" => println("starting")
case "stop" | "quit" | "exit" => println("stopping")
case _ => println("doing nothing")
}
trait Command
case object Start extends Command
case object Go extends Command
case object Stop extends Command
case object Whoa extends Command
def executeCommand(cmd: Command) = cmd match {
case Start | Go => start()
case Stop | Whoa => stop()
}
// 3.9 Assigning the Result of a Match Expression to a Variable
val evenOrOdd = someNumber match {
case 1 | 3 | 5 | 7 | 9 => println("odd")
case 2 | 4 | 6 | 8 | 10 => println("even")
}
def isTrue(a: Any) = a match {
case 0 | "" => false
case _ => true
}
// 3.10 Accessing the Value of the Default Case in a Match Expression
i match {
case 0 => println("1")
case 1 => println("2")
case default => println("You gave me: " + default)
}
i match {
case 0 => println("1")
case 1 => println("2")
case whoa => println("You gave me: " + whoa)
}
3 match {
case 1 => println("one")
case 2 => println("two") // no default match
}
// => scala.MatchError
// 3.11 Using Pattern Matching in Match Expressions
object LargeMatchTest extends App {
case class Person(firstName: String, lastName: String)
case class Dog(name: String)
def echoWhatYouGaveMe(x: Any): String = x match {
// constant patterns
case 0 => "zero"
case true => "true"
case "hello" => "you said 'hello'"
case Nil => "an empty List"
// sequence patterns
// case l: List[Int] => "List" won't work because of type erasure
case List(0, _, _) => "a three-element list with 0 as the first element"
case List(1, _*) => "a list beginning with 1, having any number of elements"
case Vector(1, _*) =>
"a vector starting with 1, having any number of elements"
// tuples
case (a, b) => s"got $a and $b"
case (a, b, c) => s"got $a, $b, and $c"
// constructor patterns
case Person(first, "Alexander") =>
s"found an Alexander, first name = $first"
case Dog("Suka") => "found a dog named Suka"
// typed patterns
case s: String => s"you gave me this string: $s"
case i: Int => s"thanks for the int: $i"
case f: Float => s"thanks for the float: $f"
case a: Array[Int] => s"an array of int: ${a.mkString(",")}"
case as: Array[String] => s"an array of strings: ${as.mkString(",")}"
case d: Dog => s"dog: ${d.name}"
case list: List[_] => s"thanks for the List: $list"
case m: Map[_, _] => m.toString
// the default wildcard pattern
case _ => "Unknown"
}
// trigger the constant patterns
println(echoWhatYouGaveMe(0))
println(echoWhatYouGaveMe(true))
println(echoWhatYouGaveMe("hello"))
println(echoWhatYouGaveMe(Nil))
// trigger the sequence patterns
println(echoWhatYouGaveMe(List(0, 1, 2)))
println(echoWhatYouGaveMe(List(1, 2)))
println(echoWhatYouGaveMe(List(1, 2, 3)))
println(echoWhatYouGaveMe(Vector(1, 2, 3)))
// trigger the tuple patterns
// two element tuple
println(echoWhatYouGaveMe((1, 2)))
// three element tuple
println(echoWhatYouGaveMe((1, 2, 3)))
// trigger the constructor patterns
println(echoWhatYouGaveMe(Person("Melissa", "Alexander")))
println(echoWhatYouGaveMe(Dog("Suka")))
// trigger the typed patterns
println(echoWhatYouGaveMe("Hello, world"))
println(echoWhatYouGaveMe(42))
println(echoWhatYouGaveMe(42F))
println(echoWhatYouGaveMe(Array(1, 2, 3)))
println(echoWhatYouGaveMe(Array("coffee", "apple pie")))
println(echoWhatYouGaveMe(Dog("Fido")))
println(echoWhatYouGaveMe(List("apple", "banana")))
println(echoWhatYouGaveMe(Map(1 -> "Al", 2 -> "Alexander")))
// trigger the wildcard pattern
println(echoWhatYouGaveMe("33d"))
}
// LargeMatchTest.main(Array())
import java.io.File
sealed trait RandomThing
case class RandomFile(f: File) extends RandomThing
case class RandomString(s: String) extends RandomThing
class RandomNoiseMaker {
def makeRandomNoise(t: RandomThing) = t match {
case RandomFile(f) => playSoundFile(f)
case RandomString(s) => speak(s)
}
}
case class Person(firstName: String, lastName: String)
object Test2 extends App {
def matchType(x: Any): String = x match {
//case x: List(1, _*) => s"$x" // doesn't compile
case x @ List(1, _*) => s"$x" // works; prints the list
//case Some(_) => "got a Some" // works, but can't access the Some
//case Some(x) => s"$x" // works, returns "foo"
case x @ Some(_) => s"$x" // works, returns "Some(foo)"
case p @ Person(first, "Doe") => s"$p" // works, returns "Person(John,Doe)"
}
println(matchType(List(1, 2, 3))) // prints "List(1, 2, 3)"
println(matchType(Some("foo"))) // prints "Some(foo)"
println(matchType(Person("John", "Doe"))) // prints "Person(John,Doe)"
}
def toInt(s: String): Option[Int] = {
try {
Some(Integer.parseInt(s.trim))
} catch {
case e: Exception => None
}
}
toInt("42") match {
case Some(i) => println(i)
case None => println("That wasn't an Int.")
}
// 3.12 Using Case Classes in Match Expressions
trait Animal
case class Dog(name: String) extends Animal
case class Cat(name: String) extends Animal
case object Woodpecker extends Animal
object CaseClassTest extends App {
def determineType(x: Animal): String = x match {
case Dog(moniker) => "Got a Dog, name = " + moniker
case _: Cat => "Got a Cat (ignoring the name)"
case Woodpecker => "That was a Woodpecker"
case _ => "That was something else"
}
println(determineType(new Dog("Rocky")))
println(determineType(new Cat("Rusty the Cat")))
println(determineType(Woodpecker))
}
// 3.13 Adding if Expressions (Guards) to Case Statements
i match {
case a if 0 to 9 contains a => println("0-9 range: " + a)
case b if 10 to 19 contains b => println("10-19 range: " + b)
case c if 20 to 29 contains c => println("20-29 range: " + c)
case _ => println("Hmmm...")
}
num match {
case x if x == 1 => println("one, a lonely number")
case x if (x == 2 || x == 3) => println(x)
case _ => println("some other value")
}
stock match {
case x if (x.symbol == "XYZ" && x.price < 20) => buy(x)
case x if (x.symbol == "XYZ" && x.price > 50) => sell(x)
case _ => // do nothing
}
def speak(p: Person) = p match {
case Person(name) if name == "Fred" => println("Yubba dubba doo")
case Person(name) if name == "Bam Bam" => println("Bam bam!")
case _ => println("Watch the Flintstones!")
}
// 3.14 Using a Match Expression Instead of isInstanceOf
def isPerson(x: Any): Boolean = x match {
case p: Person => true
case _ => false
}
trait SentientBeing
trait Animal extends SentientBeing
case class Dog(name: String) extends Animal
case class Person(name: String, age: Int) extends SentientBeing
// later in the code ...
def printInfo(x: SentientBeing) = x match {
case Person(name, age) => // handle the Person
case Dog(name) => // handle the Dog
}
// 3.15 Working with a List in a Match Expression
val x = List(1, 2, 3)
val y = 1 :: 2 :: 3 :: Nil
def listToString(list: List[String]): String = list match {
case s :: rest => s + " " + listToString(rest)
case Nil => ""
}
val fruits = "Apples" :: "Bananas" :: "Oranges" :: Nil
listToString(fruits)
def sum(list: List[Int]): Int = list match {
case Nil => 1
case n :: rest => n + sum(rest)
}
def multiply(list: List[Int]): Int = list match {
case Nil => 1
case n :: rest => n * multiply(rest)
}
val nums = List(1, 2, 3, 4, 5)
sum(nums)
multiply(nums)
// 3.16 Matching One or More Exceptions with try/catch
val s = "Foo"
try {
val i = s.toInt
} catch {
case e: Exception => e.printStackTrace
}
try { openAndReadAFile(filename) } catch {
case e: FileNotFoundException => println("Couldn't find that file.")
case e: IOException => println("Had an IOException trying to read that file")
}
try { openAndReadAFile("foo") } catch {
case t: Throwable => t.printStackTrace()
}
try {
val i = s.toInt
} catch {
case _: Throwable => println("exception ignored")
}
// nothing required here
def toInt(s: String): Option[Int] =
try {
Some(s.toInt)
} catch {
case e: Exception => throw e
}
@throws(classOf[NumberFormatException])
def toInt(s: String): Option[Int] =
try { Some(s.toInt) } catch {
case e: NumberFormatException => throw e
}
// 3.17 Declaring a Variable Before Using It in a try/catch/finally Block
import java.io._
object CopyBytes extends App {
// declared before try {}
var in = None: Option[FileInputStream]
var out = None: Option[FileOutputStream]
try {
in = Some(new FileInputStream("/tmp/Test.class"))
out = Some(new FileOutputStream("/tmp/Test.class.copy"))
var c = 0
while ({ c = in.get.read; c != -1 }) {
out.get.write(c)
}
} catch {
case e: IOException => e.printStackTrace
} finally {
println("entered finally ...")
if (in.isDefined) in.get.close
if (out.isDefined) out.get.close
}
}
try {
in = Some(new FileInputStream("/tmp/Test.class"))
out = Some(new FileOutputStream("/tmp/Test.class.copy"))
in.foreach { inputStream =>
out.foreach { outputStream =>
var c = 0
while ({ c = inputstream.read; c != -1 }) {
outputStream.write(c)
}
}
}
} catch {
case e: IOException => e.printStackTrace
} finally {
println("entered finally ...")
}
// (1) declare the null variables
var store: Store = null
var inbox: Folder = null
try {
// (2) use the variables/fields in the try block
store = session.getStore("imaps")
inbox = getFolder(store, "INBOX")
// rest of the code here ...
} catch {
case e: NoSuchProviderException => e.printStackTrace
case me: MessagingException => me.printStackTrace
} finally {
// (3) call close() on the objects in the finally clause
if (inbox != null) inbox.close
if (store != null) store.close
}
// 3.18 Creating Your Own Control Structures
package com.alvinalexander.controls
import scala.annotation.tailrec
object Whilst {
@tailrec
def whilst(testCondition: => Boolean)(codeBlock: => Unit) {
if (testCondition) {
codeBlock
whilst(testCondition)(codeBlock)
}
}
}
package foo
import com.alvinalexander.controls.Whilst._
object WhilstDemo extends App {
var i = 0
whilst(i < 5) {
println(i)
i += 1
}
}
// two 'if' condition tests
def doubleif(test1: => Boolean)(test2: => Boolean)(codeBlock: => Unit) {
if (test1 && test2) {
codeBlock
}
}
doubleif(age > 18)(numAccidents == 0) { println("Discount!") }