119 val lst = List(None, Some(1), Some(2), None, Some(3)) |
122 val lst = List(None, Some(1), Some(2), None, Some(3)) |
120 |
123 |
121 for (x <- lst) yield x.getOrElse(0) |
124 for (x <- lst) yield x.getOrElse(0) |
122 |
125 |
123 |
126 |
|
127 // a function that turns strings into numbers (similar to .toInt) |
|
128 Integer.parseInt("1234") |
|
129 |
|
130 |
|
131 def get_me_an_int(s: String) : Option[Int] = |
|
132 Try(Some(Integer.parseInt(s))).getOrElse(None) |
124 |
133 |
125 |
134 |
126 // This may not look any better than working with null in Java, but to |
135 // This may not look any better than working with null in Java, but to |
127 // see the value, you have to put yourself in the shoes of the |
136 // see the value, you have to put yourself in the shoes of the |
128 // consumer of the get_me_an_int function, and imagine you didn't |
137 // consumer of the get_me_an_int function, and imagine you didn't |
129 // write that function. |
138 // write that function. |
130 // |
139 // |
131 // In Java, if you didn't write this function, you'd have to depend on |
140 // In Java, if you didn't write this function, you'd have to depend on |
132 // the Javadoc of the get_me_an_int. If you didn't look at the Javadoc, |
141 // the Javadoc of the get_me_an_int. If you didn't look at the Javadoc, |
133 // you might not know that get_me_an_int could return a null, and your |
142 // you might not know that get_me_an_int could return null, and your |
134 // code could potentially throw a NullPointerException. |
143 // code could potentially throw a NullPointerException. |
135 |
|
136 |
144 |
137 |
145 |
138 // even Scala is not immune to problems like this: |
146 // even Scala is not immune to problems like this: |
139 |
147 |
140 List(5,6,7,8,9).indexOf(7) |
148 List(5,6,7,8,9).indexOf(7) |
152 def even(x: Int) : Boolean = x % 2 == 0 |
160 def even(x: Int) : Boolean = x % 2 == 0 |
153 def odd(x: Int) : Boolean = x % 2 == 1 |
161 def odd(x: Int) : Boolean = x % 2 == 1 |
154 |
162 |
155 val lst = (1 to 10).toList |
163 val lst = (1 to 10).toList |
156 |
164 |
157 lst.filter(x => even(x)) |
|
158 lst.filter(even(_)) |
|
159 lst.filter(even) |
165 lst.filter(even) |
160 |
|
161 lst.count(even) |
166 lst.count(even) |
162 |
|
163 |
|
164 lst.find(even) |
167 lst.find(even) |
165 |
168 |
166 val ps = List((3, 0), (3, 2), (4, 2), (2, 2), (2, 0), (1, 1), (1, 0)) |
169 lst.filter(x => x % 2 == 0) |
|
170 lst.filter(_ % 2 == 0) |
167 |
171 |
168 lst.sortWith(_ > _) |
172 lst.sortWith(_ > _) |
169 lst.sortWith(_ < _) |
173 lst.sortWith(_ < _) |
170 |
174 |
|
175 // but this only works when the arguments are clear, but |
|
176 // not with multiple occurences |
|
177 lst.find(n => odd(n) && n > 2) |
|
178 |
|
179 |
|
180 val ps = List((3, 0), (3, 2), (4, 2), (2, 2), (2, 0), (1, 1), (1, 0)) |
|
181 |
171 def lex(x: (Int, Int), y: (Int, Int)) : Boolean = |
182 def lex(x: (Int, Int), y: (Int, Int)) : Boolean = |
172 if (x._1 == y._1) x._2 < y._2 else x._1 < y._1 |
183 if (x._1 == y._1) x._2 < y._2 else x._1 < y._1 |
173 |
184 |
174 ps.sortWith(lex) |
185 ps.sortWith(lex) |
175 |
186 |
245 def sum_cubes(lst: List[Int]) = sumOf(x => x * x * x, lst) |
254 def sum_cubes(lst: List[Int]) = sumOf(x => x * x * x, lst) |
246 |
255 |
247 sum_squares(lst) |
256 sum_squares(lst) |
248 sum_cubes(lst) |
257 sum_cubes(lst) |
249 |
258 |
250 // lets try it factorial |
259 // lets try a factorial |
251 def fact(n: Int) : Int = |
260 def fact(n: Int) : Int = |
252 if (n == 0) 1 else n * fact(n - 1) |
261 if (n == 0) 1 else n * fact(n - 1) |
253 |
262 |
254 def sum_fact(lst: List[Int]) = sumOf(fact, lst) |
263 def sum_fact(lst: List[Int]) = sumOf(fact, lst) |
255 sum_fact(lst) |
264 sum_fact(lst) |
256 |
265 |
257 |
266 |
258 |
267 |
259 // if you like verbosity, you can full-specify the literal. |
268 // sometimes it is needed that you specify the type. |
260 // Don't go telling that to people, though |
|
261 (1 to 100).filter((x: Int) => x % 2 == 0).sum |
269 (1 to 100).filter((x: Int) => x % 2 == 0).sum |
262 |
270 |
263 // As x is known to be an Int anyway, you can omit that part |
271 // in this case it is clear that x mist be an Int |
264 (1 to 100).filter(x => x % 2 == 0).sum |
272 (1 to 100).filter(x => x % 2 == 0).sum |
265 |
273 |
266 // As each parameter (only x in this case) is passed only once |
274 // As each parameter (only x in this case) is passed only once |
267 // you can use the wizardy placeholder syntax |
275 // you can use the wizardy placeholder syntax |
268 (1 to 100).filter(_ % 2 == 0).sum |
276 (1 to 100).filter(_ % 2 == 0).sum |
269 |
277 |
270 // But if you want to re-use your literal, you can also put it in a value |
278 |
271 // In this case, explicit types are required because there's nothing to infer from |
279 |
272 val isEven = (x: Int) => x % 2 == 0 |
280 // Option Type and maps |
273 (1 to 100).filter(isEven).sum |
281 //====================== |
274 |
|
275 |
|
276 |
|
277 // Option Type again |
|
278 //=================== |
|
279 |
282 |
280 // a function that turns strings into numbers (similar to .toInt) |
283 // a function that turns strings into numbers (similar to .toInt) |
281 Integer.parseInt("12u34") |
284 Integer.parseInt("12u34") |
282 |
285 |
|
286 import scala.util._ |
283 |
287 |
284 def get_me_an_int(s: String) : Option[Int] = |
288 def get_me_an_int(s: String) : Option[Int] = |
285 Try(Some(Integer.parseInt(s))).getOrElse(None) |
289 Try(Some(Integer.parseInt(s))).getOrElse(None) |
286 |
290 |
287 val lst = List("12345", "foo", "5432", "bar", "x21", "456") |
291 val lst = List("12345", "foo", "5432", "bar", "x21", "456") |
413 |
415 |
414 for (n <- 0 to 20) |
416 for (n <- 0 to 20) |
415 println(fizz_buzz(n)) |
417 println(fizz_buzz(n)) |
416 |
418 |
417 |
419 |
418 // User-defined Datatypes |
|
419 //======================== |
|
420 |
|
421 |
|
422 abstract class Colour |
|
423 case object Red extends Colour |
|
424 case object Green extends Colour |
|
425 case object Blue extends Colour |
|
426 |
|
427 def fav_colour(c: Colour) : Boolean = c match { |
|
428 case Red => false |
|
429 case Green => true |
|
430 case Blue => false |
|
431 } |
|
432 |
|
433 fav_colour(Green) |
|
434 |
|
435 |
|
436 // ... a tiny bit more useful: Roman Numerals |
|
437 |
|
438 abstract class RomanDigit |
|
439 case object I extends RomanDigit |
|
440 case object V extends RomanDigit |
|
441 case object X extends RomanDigit |
|
442 case object L extends RomanDigit |
|
443 case object C extends RomanDigit |
|
444 case object D extends RomanDigit |
|
445 case object M extends RomanDigit |
|
446 |
|
447 type RomanNumeral = List[RomanDigit] |
|
448 |
|
449 List(X,I) |
|
450 |
|
451 /* |
|
452 I -> 1 |
|
453 II -> 2 |
|
454 III -> 3 |
|
455 IV -> 4 |
|
456 V -> 5 |
|
457 VI -> 6 |
|
458 VII -> 7 |
|
459 VIII -> 8 |
|
460 IX -> 9 |
|
461 X -> X |
|
462 */ |
|
463 |
|
464 def RomanNumeral2Int(rs: RomanNumeral): Int = rs match { |
|
465 case Nil => 0 |
|
466 case M::r => 1000 + RomanNumeral2Int(r) |
|
467 case C::M::r => 900 + RomanNumeral2Int(r) |
|
468 case D::r => 500 + RomanNumeral2Int(r) |
|
469 case C::D::r => 400 + RomanNumeral2Int(r) |
|
470 case C::r => 100 + RomanNumeral2Int(r) |
|
471 case X::C::r => 90 + RomanNumeral2Int(r) |
|
472 case L::r => 50 + RomanNumeral2Int(r) |
|
473 case X::L::r => 40 + RomanNumeral2Int(r) |
|
474 case X::r => 10 + RomanNumeral2Int(r) |
|
475 case I::X::r => 9 + RomanNumeral2Int(r) |
|
476 case V::r => 5 + RomanNumeral2Int(r) |
|
477 case I::V::r => 4 + RomanNumeral2Int(r) |
|
478 case I::r => 1 + RomanNumeral2Int(r) |
|
479 } |
|
480 |
|
481 RomanNumeral2Int(List(I,V)) // 4 |
|
482 RomanNumeral2Int(List(I,I,I,I)) // 4 (invalid Roman number) |
|
483 RomanNumeral2Int(List(V,I)) // 6 |
|
484 RomanNumeral2Int(List(I,X)) // 9 |
|
485 RomanNumeral2Int(List(M,C,M,L,X,X,I,X)) // 1979 |
|
486 RomanNumeral2Int(List(M,M,X,V,I,I)) // 2017 |
|
487 |
|
488 |
|
489 // another example |
|
490 //================= |
|
491 |
|
492 // Once upon a time, in a complete fictional |
|
493 // country there were Persons... |
|
494 |
|
495 |
|
496 abstract class Person |
|
497 case object King extends Person |
|
498 case class Peer(deg: String, terr: String, succ: Int) extends Person |
|
499 case class Knight(name: String) extends Person |
|
500 case class Peasant(name: String) extends Person |
|
501 |
|
502 |
|
503 def title(p: Person): String = p match { |
|
504 case King => "His Majesty the King" |
|
505 case Peer(deg, terr, _) => s"The ${deg} of ${terr}" |
|
506 case Knight(name) => s"Sir ${name}" |
|
507 case Peasant(name) => name |
|
508 } |
|
509 |
|
510 def superior(p1: Person, p2: Person): Boolean = (p1, p2) match { |
|
511 case (King, _) => true |
|
512 case (Peer(_,_,_), Knight(_)) => true |
|
513 case (Peer(_,_,_), Peasant(_)) => true |
|
514 case (Peer(_,_,_), Clown) => true |
|
515 case (Knight(_), Peasant(_)) => true |
|
516 case (Knight(_), Clown) => true |
|
517 case (Clown, Peasant(_)) => true |
|
518 case _ => false |
|
519 } |
|
520 |
|
521 val people = List(Knight("David"), |
|
522 Peer("Duke", "Norfolk", 84), |
|
523 Peasant("Christian"), |
|
524 King, |
|
525 Clown) |
|
526 |
|
527 println(people.sortWith(superior).mkString("\n")) |
|
528 |
|
529 |
|
530 // String interpolations as patterns |
|
531 |
|
532 val date = "2000-01-01" |
|
533 val s"$year-$month-$day" = date |
|
534 |
|
535 def parse_date(date: String) = date match { |
|
536 case s"$year-$month-$day" => Some((year.toInt, month.toInt, day.toInt)) |
|
537 case s"$day/$month/$year" => Some((year.toInt, month.toInt, day.toInt)) |
|
538 case _ => None |
|
539 } |
|
540 |
420 |
541 |
421 |
542 // Recursion |
422 // Recursion |
543 //=========== |
423 //=========== |
544 |
424 |
545 /* a, b, c |
425 // well-known example |
546 |
426 |
547 aa aaa |
427 def fib(n: Int) : Int = { |
548 ab baa |
428 if (n == 0 || n == 1) 1 |
549 ac caa |
429 else fib(n - 1) + fib(n - 2) |
550 ba => ...... |
430 } |
551 bb |
431 |
552 bc |
432 |
553 ca |
433 /* Say you have characters a, b, c. |
554 cb |
434 What are all the combinations of a certain length? |
555 cc |
435 |
556 |
436 All combinations of length 2: |
|
437 |
|
438 aa, ab, ac, ba, bb, bc, ca, cb, cc |
|
439 |
|
440 Combinations of length 3: |
|
441 |
|
442 aaa, baa, caa, and so on...... |
557 */ |
443 */ |
558 |
444 |
559 def perms(cs: List[Char], l: Int) : List[String] = { |
445 def combs(cs: List[Char], l: Int) : List[String] = { |
560 if (l == 0) List("") |
446 if (l == 0) List("") |
561 else for (c <- cs; s <- perms(cs, l - 1)) yield s"$c$s" |
447 else for (c <- cs; s <- combs(cs, l - 1)) yield s"$c$s" |
562 } |
448 } |
563 |
449 |
564 perms("abc".toList, 2) |
450 combs("abc".toList, 2) |
|
451 |
|
452 |
|
453 // another well-known example |
565 |
454 |
566 def move(from: Char, to: Char) = |
455 def move(from: Char, to: Char) = |
567 println(s"Move disc from $from to $to!") |
456 println(s"Move disc from $from to $to!") |
568 |
457 |
569 def hanoi(n: Int, from: Char, via: Char, to: Char) : Unit = { |
458 def hanoi(n: Int, from: Char, via: Char, to: Char) : Unit = { |
573 move(from, to) |
462 move(from, to) |
574 hanoi(n - 1, via, from, to) |
463 hanoi(n - 1, via, from, to) |
575 } |
464 } |
576 } |
465 } |
577 |
466 |
578 hanoi(40, 'A', 'B', 'C') |
467 hanoi(4, 'A', 'B', 'C') |
579 |
468 |
580 |
469 |
581 // Tail Recursion |
470 // A Recursive Web Crawler / Email Harvester |
582 //================ |
471 //=========================================== |
583 |
|
584 |
|
585 def fact(n: Long): Long = |
|
586 if (n == 0) 1 else n * fact(n - 1) |
|
587 |
|
588 fact(10) //ok |
|
589 fact(10000) // produces a stackoverflow |
|
590 |
|
591 def factT(n: BigInt, acc: BigInt): BigInt = |
|
592 if (n == 0) acc else factT(n - 1, n * acc) |
|
593 |
|
594 factT(10, 1) |
|
595 factT(100000, 1) |
|
596 |
|
597 // there is a flag for ensuring a function is tail recursive |
|
598 import scala.annotation.tailrec |
|
599 |
|
600 @tailrec |
|
601 def factT(n: BigInt, acc: BigInt): BigInt = |
|
602 if (n == 0) acc else factT(n - 1, n * acc) |
|
603 |
|
604 |
|
605 |
|
606 // for tail-recursive functions the Scala compiler |
|
607 // generates loop-like code, which does not need |
|
608 // to allocate stack-space in each recursive |
|
609 // call; Scala can do this only for tail-recursive |
|
610 // functions |
|
611 |
|
612 |
|
613 // A Web Crawler / Email Harvester |
|
614 //================================= |
|
615 // |
472 // |
616 // the idea is to look for links using the |
473 // the idea is to look for links using the |
617 // regular expression "https?://[^"]*" and for |
474 // regular expression "https?://[^"]*" and for |
618 // email addresses using another regex. |
475 // email addresses using another regex. |
619 |
476 |
641 def get_all_URLs(page: String): Set[String] = |
498 def get_all_URLs(page: String): Set[String] = |
642 http_pattern.findAllIn(page).map(unquote).toSet |
499 http_pattern.findAllIn(page).map(unquote).toSet |
643 |
500 |
644 // naive version of crawl - searches until a given depth, |
501 // naive version of crawl - searches until a given depth, |
645 // visits pages potentially more than once |
502 // visits pages potentially more than once |
646 def crawl(url: String, n: Int) : Set[String] = { |
503 def crawl(url: String, n: Int) : Unit = { |
|
504 if (n == 0) () |
|
505 else { |
|
506 println(s" Visiting: $n $url") |
|
507 for (u <- get_all_URLs(get_page(url))) crawl(u, n - 1) |
|
508 } |
|
509 } |
|
510 |
|
511 // some starting URLs for the crawler |
|
512 val startURL = """https://nms.kcl.ac.uk/christian.urban/""" |
|
513 |
|
514 crawl(startURL, 2) |
|
515 |
|
516 |
|
517 // a primitive email harvester |
|
518 def emails(url: String, n: Int) : Set[String] = { |
647 if (n == 0) Set() |
519 if (n == 0) Set() |
648 else { |
520 else { |
649 println(s" Visiting: $n $url") |
521 println(s" Visiting: $n $url") |
650 val page = get_page(url) |
522 val page = get_page(url) |
651 val new_emails = email_pattern.findAllIn(page).toSet |
523 val new_emails = email_pattern.findAllIn(page).toSet |
652 new_emails ++ (for (u <- get_all_URLs(page)) yield crawl(u, n - 1)).flatten |
524 new_emails ++ (for (u <- get_all_URLs(page)) yield emails(u, n - 1)).flatten |
653 } |
525 } |
654 } |
526 } |
655 |
527 |
656 // some starting URLs for the crawler |
528 emails(startURL, 3) |
657 val startURL = """https://nms.kcl.ac.uk/christian.urban/""" |
529 |
658 |
530 |
659 crawl(startURL, 2) |
531 // if we want to explore the internet "deeper", then we |
660 |
532 // first have to parallelise the request of webpages: |
661 |
533 // |
662 |
534 // scala -cp scala-parallel-collections_2.13-0.2.0.jar |
663 |
535 // import scala.collection.parallel.CollectionConverters._ |
664 |
536 |
665 |
537 |
666 |
538 |
667 // Sudoku |
539 |
668 //======== |
540 |
669 |
541 |
670 // THE POINT OF THIS CODE IS NOT TO BE SUPER |
|
671 // EFFICIENT AND FAST, just explaining exhaustive |
|
672 // depth-first search |
|
673 |
|
674 |
|
675 val game0 = """.14.6.3.. |
|
676 |62...4..9 |
|
677 |.8..5.6.. |
|
678 |.6.2....3 |
|
679 |.7..1..5. |
|
680 |5....9.6. |
|
681 |..6.2..3. |
|
682 |1..5...92 |
|
683 |..7.9.41.""".stripMargin.replaceAll("\\n", "") |
|
684 |
|
685 type Pos = (Int, Int) |
|
686 val emptyValue = '.' |
|
687 val maxValue = 9 |
|
688 |
|
689 val allValues = "123456789".toList |
|
690 val indexes = (0 to 8).toList |
|
691 |
|
692 |
|
693 def empty(game: String) = game.indexOf(emptyValue) |
|
694 def isDone(game: String) = empty(game) == -1 |
|
695 def emptyPosition(game: String) : Pos = |
|
696 (empty(game) % maxValue, empty(game) / maxValue) |
|
697 |
|
698 |
|
699 def get_row(game: String, y: Int) = indexes.map(col => game(y * maxValue + col)) |
|
700 def get_col(game: String, x: Int) = indexes.map(row => game(x + row * maxValue)) |
|
701 |
|
702 def get_box(game: String, pos: Pos): List[Char] = { |
|
703 def base(p: Int): Int = (p / 3) * 3 |
|
704 val x0 = base(pos._1) |
|
705 val y0 = base(pos._2) |
|
706 for (x <- (x0 until x0 + 3).toList; |
|
707 y <- (y0 until y0 + 3).toList) yield game(x + y * maxValue) |
|
708 } |
|
709 |
|
710 |
|
711 //get_row(game0, 0) |
|
712 //get_row(game0, 1) |
|
713 //get_box(game0, (3,1)) |
|
714 |
|
715 def update(game: String, pos: Int, value: Char): String = |
|
716 game.updated(pos, value) |
|
717 |
|
718 def toAvoid(game: String, pos: Pos): List[Char] = |
|
719 (get_col(game, pos._1) ++ get_row(game, pos._2) ++ get_box(game, pos)) |
|
720 |
|
721 def candidates(game: String, pos: Pos): List[Char] = |
|
722 allValues.diff(toAvoid(game, pos)) |
|
723 |
|
724 //candidates(game0, (0, 0)) |
|
725 |
|
726 def pretty(game: String): String = |
|
727 "\n" ++ (game.sliding(maxValue, maxValue).mkString("\n")) |
|
728 |
|
729 def search(game: String): List[String] = { |
|
730 if (isDone(game)) List(game) |
|
731 else |
|
732 candidates(game, emptyPosition(game)). |
|
733 map(c => search(update(game, empty(game), c))).flatten |
|
734 } |
|
735 |
|
736 // an easy game |
|
737 val game1 = """23.915... |
|
738 |...2..54. |
|
739 |6.7...... |
|
740 |..1.....9 |
|
741 |89.5.3.17 |
|
742 |5.....6.. |
|
743 |......9.5 |
|
744 |.16..7... |
|
745 |...329..1""".stripMargin.replaceAll("\\n", "") |
|
746 |
|
747 |
|
748 // a game that is in the sligtly harder category |
|
749 val game2 = """8........ |
|
750 |..36..... |
|
751 |.7..9.2.. |
|
752 |.5...7... |
|
753 |....457.. |
|
754 |...1...3. |
|
755 |..1....68 |
|
756 |..85...1. |
|
757 |.9....4..""".stripMargin.replaceAll("\\n", "") |
|
758 |
|
759 // a game with multiple solutions |
|
760 val game3 = """.8...9743 |
|
761 |.5...8.1. |
|
762 |.1....... |
|
763 |8....5... |
|
764 |...8.4... |
|
765 |...3....6 |
|
766 |.......7. |
|
767 |.3.5...8. |
|
768 |9724...5.""".stripMargin.replaceAll("\\n", "") |
|
769 |
|
770 |
|
771 search(game0).map(pretty) |
|
772 search(game1).map(pretty) |
|
773 |
|
774 // for measuring time |
|
775 def time_needed[T](i: Int, code: => T) = { |
|
776 val start = System.nanoTime() |
|
777 for (j <- 1 to i) code |
|
778 val end = System.nanoTime() |
|
779 s"${(end - start) / i / 1.0e9} secs" |
|
780 } |
|
781 |
|
782 search(game2).map(pretty) |
|
783 search(game3).distinct.length |
|
784 time_needed(3, search(game2)) |
|
785 time_needed(3, search(game3)) |
|
786 |
|
787 |
|
788 |
|
789 |
|
790 |
|
791 |
|