├── .gitignore ├── LICENSE ├── README.md ├── build.sbt ├── project ├── Dependencies.scala └── build.properties └── src ├── main ├── resources │ └── access_log └── scala │ └── com │ └── workshop │ ├── Collections.scala │ ├── DuplicatesRemover.scala │ ├── ExpressionEvaluator.scala │ ├── Fibonacci.scala │ ├── FizzBuz.scala │ ├── FrequencyMap.scala │ ├── FunctionsComposition.scala │ ├── FunctionsHigherOrder.scala │ ├── HelloWorld.scala │ ├── IsIncreasing.scala │ ├── LogAnalyzer.scala │ ├── LongestSubstring.scala │ ├── PersonTitle.scala │ ├── Quadratic.scala │ └── QuadraticJ.java └── test └── scala └── com └── workshop ├── CollectionsTest.scala ├── DuplicatesRemoverTest.scala ├── ExpressionEvaluatorTest.scala ├── FibonacciTest.scala ├── FizzBuzTest.scala ├── FrequencyMapTest.scala ├── FunctionsCompositionTest.scala ├── FunctionsHigherOrderTest.scala ├── IsIncreasingTest.scala ├── LongestSubstringTest.scala ├── PersonTitleTest.scala └── QuadraticTest.scala /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Scala template 3 | *.class 4 | *.log 5 | ### JetBrains template 6 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 7 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 8 | 9 | # User-specific stuff: 10 | .idea/**/workspace.xml 11 | .idea/**/tasks.xml 12 | .idea/dictionaries 13 | .idea 14 | 15 | # Sensitive or high-churn files: 16 | .idea/**/dataSources/ 17 | .idea/**/dataSources.ids 18 | .idea/**/dataSources.xml 19 | .idea/**/dataSources.local.xml 20 | .idea/**/sqlDataSources.xml 21 | .idea/**/dynamic.xml 22 | .idea/**/uiDesigner.xml 23 | 24 | # Gradle: 25 | .idea/**/gradle.xml 26 | .idea/**/libraries 27 | 28 | # CMake 29 | cmake-build-debug/ 30 | 31 | # Mongo Explorer plugin: 32 | .idea/**/mongoSettings.xml 33 | 34 | ## File-based project format: 35 | *.iws 36 | 37 | ## Plugin-specific files: 38 | 39 | # IntelliJ 40 | out/ 41 | 42 | # mpeltonen/sbt-idea plugin 43 | .idea_modules/ 44 | 45 | # JIRA plugin 46 | atlassian-ide-plugin.xml 47 | 48 | # Cursive Clojure plugin 49 | .idea/replstate.xml 50 | 51 | # Crashlytics plugin (for Android Studio and IntelliJ) 52 | com_crashlytics_export_strings.xml 53 | crashlytics.properties 54 | crashlytics-build.properties 55 | fabric.properties 56 | ### SBT template 57 | # Simple Build Tool 58 | # http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control 59 | 60 | dist/* 61 | target/ 62 | lib_managed/ 63 | src_managed/ 64 | project/boot/ 65 | project/plugins/project/ 66 | .history 67 | .cache 68 | .lib/ 69 | project/target 70 | /target 71 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Maxim Novak 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # scala-workshop 2 | Scala Workshop (Beginners) 3 | 4 | ### Presentation - 5 | [The Joy of Scala talk (JEEConf) - English](https://www.youtube.com/watch?v=GeG9VPmUa28) 6 | 7 | [The Joy of Scala talk (JavaIL) - Hebrew](https://www.youtube.com/watch?v=TcnYTwff2xU) 8 | 9 | [Slides](https://www.slideshare.net/maximnovak/joy-of-scala) 10 | 11 | ### Some more coding exercises - 12 | [Coding exersices with solutions](https://github.com/maximn/coding-interview-questions-scala) 13 | 14 | [TaxCalculator](https://github.com/maximn/TaxCalculator) 15 | 16 | ### More resources - 17 | [Twitter Scala School](http://twitter.github.io/scala_school/) 18 | 19 | [The Neophyte's Guide to Scala](http://danielwestheide.com/scala/neophytes.html) 20 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | lazy val root = (project in file(".")). 2 | settings( 3 | inThisBuild(List( 4 | organization := "com.workshop", 5 | scalaVersion := "2.12.3", 6 | version := "0.1.0-SNAPSHOT" 7 | )), 8 | name := "Scala Workshop", 9 | libraryDependencies ++= Seq( 10 | "joda-time" % "joda-time" % "2.9.9", 11 | "org.specs2" %% "specs2-core" % "3.9.4" % Test, 12 | "org.specs2" %% "specs2-scalacheck" % "3.9.4" % Test 13 | ) 14 | ) 15 | -------------------------------------------------------------------------------- /project/Dependencies.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | 3 | object Dependencies { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.13 2 | -------------------------------------------------------------------------------- /src/main/scala/com/workshop/Collections.scala: -------------------------------------------------------------------------------- 1 | package com.workshop 2 | 3 | object Collections { 4 | // map (1, 2, 3) -> (2, 4, 6) 5 | def doubleEachElement(seq: Seq[Int]): Seq[Int] = ??? 6 | 7 | // filter 8 | def onlySmaller(seq: Seq[Int], smallerThan: Int): Seq[Int] = ??? 9 | 10 | // filter 11 | def keepOddNumberOnly(seq: Seq[Int]): Seq[Int] = ??? 12 | 13 | // maxBy 14 | def longest(seq: Seq[String]): String = ??? 15 | 16 | // forAll, Char.isUpper 17 | def isAllUpper(str: String): Boolean = ??? 18 | 19 | // zipWithIndex, filter, map. filter&map = collect 20 | def oddPositionsZeroBased(seq: Seq[Int]): Seq[Int] = ??? 21 | 22 | // recursion - make a call that will remove an item and put on the other side of the Seq 23 | def reverse[T](seq: Seq[T]): Seq[T] = ??? 24 | } 25 | -------------------------------------------------------------------------------- /src/main/scala/com/workshop/DuplicatesRemover.scala: -------------------------------------------------------------------------------- 1 | package com.workshop 2 | 3 | // wanna try to do it in an immutable way? 4 | // go over the list recursively - 5 | // for each item check if in contained in a `seen` Set 6 | // if no - add the item to the `seen` list and to the result Seq 7 | // yes - skip it and continue traversing the Seq 8 | class DuplicatesRemover { 9 | def remove(seq: Seq[Int]): Seq[Int] = ??? 10 | } 11 | -------------------------------------------------------------------------------- /src/main/scala/com/workshop/ExpressionEvaluator.scala: -------------------------------------------------------------------------------- 1 | package com.workshop 2 | 3 | 4 | sealed trait Expression 5 | case class Const(n: Int) extends Expression 6 | case class Sum(a: Expression, b: Expression) extends Expression 7 | 8 | // Use match case 9 | // You know how to evaluate `Const` 10 | // Sum of expressions is the sum of the evaluation of these expressions 11 | object ExpressionEvaluator { 12 | def evaluate(expr: Expression): Int = ??? 13 | } 14 | -------------------------------------------------------------------------------- /src/main/scala/com/workshop/Fibonacci.scala: -------------------------------------------------------------------------------- 1 | package com.workshop 2 | 3 | // Let's do a recursive solution 4 | // Use match on the `n` 5 | // You can use `|` to match on multiple constants 6 | // you can use guard `case x if ... =>` 7 | class Fibonacci { 8 | def nth(n: Int): Int = ??? 9 | } 10 | -------------------------------------------------------------------------------- /src/main/scala/com/workshop/FizzBuz.scala: -------------------------------------------------------------------------------- 1 | package com.workshop 2 | 3 | // match on a tuple (divisibleBy3, divisibleBy5) 4 | object FizzBuz { 5 | def eval(n: Int): String = ??? 6 | } 7 | -------------------------------------------------------------------------------- /src/main/scala/com/workshop/FrequencyMap.scala: -------------------------------------------------------------------------------- 1 | package com.workshop 2 | 3 | // String can be used as a Seq[Char] 4 | // groupBy works on a Seq. You provide a function that defines a key for each element. 5 | // Then it builds a map Key -> Seq(Elements with that key) 6 | // mapValues works on a map and run a function on all the values on the map. 7 | class FrequencyMap { 8 | def generate(str: String): Map[Char, Int] = ??? 9 | } 10 | -------------------------------------------------------------------------------- /src/main/scala/com/workshop/FunctionsComposition.scala: -------------------------------------------------------------------------------- 1 | package com.workshop 2 | 3 | object FunctionsComposition { 4 | // Functions Composition 5 | val toLowerCase: String => String = _.toLowerCase 6 | val capitalizeWords: String => String = 7 | (value: String) => value.split(" ").map(word => word.take(1).toUpperCase + word.substring(1)).mkString(" ") 8 | val removeSpaces: String => String = _.replace(" ", "") 9 | 10 | val camelCase: (String) => String = 11 | toLowerCase 12 | .andThen(capitalizeWords) 13 | .andThen(removeSpaces) 14 | 15 | 16 | //Now your turn 17 | val price = 10 18 | 19 | def taxCalculatorFactory(taxPercentage: Int): Double => Double = _ * (1 + taxPercentage / 100d) 20 | 21 | def taxCalculatorFor(country: String): Double => Double = country match { 22 | case "IL" => taxCalculatorFactory(18) 23 | case "UA" => taxCalculatorFactory(20) 24 | } 25 | 26 | val applyTax: Double => Double = taxCalculatorFor("UA") 27 | val applyDiscount: Double => Double = _ * 0.8 28 | val applyShipping: Double => Double = _ + 10 29 | 30 | // use .andThen to create the function as a combination of existing functions 31 | val calculatePriceThenShipping: Double => Double = _ => ??? 32 | 33 | // use .compose (opposite order of andThen). Note: Now we're using `def` for the function deceleration 34 | def addShippingThenCalculatePrice(price: Double): Double = ??? 35 | } -------------------------------------------------------------------------------- /src/main/scala/com/workshop/FunctionsHigherOrder.scala: -------------------------------------------------------------------------------- 1 | package com.workshop 2 | 3 | object FunctionsHigherOrder { 4 | // Higher-Order functions - function that return function 5 | // Functions are 1st class citizen - a val can be also a function 6 | 7 | // This is just a syntactic sugar, so we don't need to type the method definition each time 8 | type BinaryOperation = (Double, Double) => Double 9 | 10 | val binaryOperationFactory: (Double, Double, BinaryOperation) => Double = 11 | (a: Double, b: Double, operation: BinaryOperation) => operation(a, b) 12 | val plus: BinaryOperation = _ + _ 13 | val minus: BinaryOperation = _ - _ 14 | val sum: (Double, Double) => Double = binaryOperationFactory(_, _, plus) 15 | 16 | def minus(a: Double, b: Double): Double = binaryOperationFactory(a, b, minus) 17 | 18 | // create the multiply function using `binaryOperation` 19 | val multiply: (Double, Double) => Double = (_, _) => ??? 20 | 21 | // create the plusOne function (use sum?) 22 | val plusOne: Double => Double = _ => ??? 23 | 24 | 25 | val numbers = Seq(1, 2, 3, 4) 26 | 27 | // Could we do it better? 28 | // Create a sumSeqFactory that receives the operation and returns a function that will do this 29 | // operation on all elements of the Seq before summing 30 | // Change the implementation of sumSeq, sumDoubles, sumSquares to use `sumSeqFactory` 31 | val sumSeq: (Seq[Int]) => Int = _.map(identity).sum 32 | 33 | val sumDoubles: (Seq[Int]) => Int = _.map(_ * 2).sum 34 | 35 | val sumSquares: (Seq[Int]) => Int = _.map(number => Math.pow(number, 2).toInt).sum 36 | } 37 | -------------------------------------------------------------------------------- /src/main/scala/com/workshop/HelloWorld.scala: -------------------------------------------------------------------------------- 1 | package com.workshop 2 | 3 | object HelloWorld extends App { 4 | println("Hello World") 5 | } 6 | -------------------------------------------------------------------------------- /src/main/scala/com/workshop/IsIncreasing.scala: -------------------------------------------------------------------------------- 1 | package com.workshop 2 | 3 | // You can use recursion to check the first two items if increasing, then call recursively without the element that changed. 4 | // Another option, use sliding window (.sliding) 5 | class IsIncreasing[T](isSmaller: (T, T) => Boolean) { 6 | def check(seq: Seq[T]): Boolean = ??? 7 | } 8 | -------------------------------------------------------------------------------- /src/main/scala/com/workshop/LogAnalyzer.scala: -------------------------------------------------------------------------------- 1 | package com.workshop 2 | 3 | import java.util.Locale 4 | 5 | import org.joda.time.DateTime 6 | import org.joda.time.format.DateTimeFormat 7 | 8 | import scala.io.Source 9 | 10 | // define a case class that represents a log line 11 | 12 | object LogAnalyzer extends App { 13 | //Read from the log file 14 | val log = Source.fromInputStream(getClass.getResourceAsStream("/access_log")).getLines() 15 | 16 | // map each line to a case class that represents it 17 | // Split? Regex? 18 | // How to parse the datetime? 19 | // DateTime.parse(dateTimeString, 20 | // DateTimeFormat 21 | // .forPattern("dd/MMM/yyyy:HH:mm:ssZ") 22 | // .withLocale(Locale.US)) 23 | val logLines = log.map { ??? } 24 | 25 | 26 | // how many web requests? 27 | // how many of each status code? 28 | // how many distinct IPs? Wait, did you use String for IP? Maybe use a case class that will validate? 29 | // Maybe some lines are bad? We want to throw them away. (When parsing return Try/Option, use flatMap instead of map on the log lines) 30 | // Largest response size? What was the request for it? Average response size? Sum of all responses? 31 | // which url have most hits? How many hits? 32 | 33 | println("Use println for output") 34 | } -------------------------------------------------------------------------------- /src/main/scala/com/workshop/LongestSubstring.scala: -------------------------------------------------------------------------------- 1 | package com.workshop 2 | 3 | //longest substring that contains uppercase&lowercase but not numbers 4 | // aV3rwerwr6Wix -> Wix 5 | object LongestSubstring { 6 | // split - regex "[0-9]" to split by num 7 | // filter - filter strings that have (exists) upper & lower. 8 | // maxBy - take longest 9 | def find(str: String): String = ??? 10 | } 11 | -------------------------------------------------------------------------------- /src/main/scala/com/workshop/PersonTitle.scala: -------------------------------------------------------------------------------- 1 | package com.workshop 2 | 3 | object PersonTitle { 4 | // match on tuple of (gender, status) 5 | // take advantage of extractors 6 | def genderTitle(gender: Gender, status: Option[Status]): String = ??? 7 | } 8 | 9 | sealed trait Gender 10 | 11 | case object Male extends Gender 12 | 13 | case object Female extends Gender 14 | 15 | sealed trait Status 16 | 17 | case object Married extends Status 18 | 19 | case object Single extends Status 20 | 21 | -------------------------------------------------------------------------------- /src/main/scala/com/workshop/Quadratic.scala: -------------------------------------------------------------------------------- 1 | package com.workshop 2 | 3 | object Quadratic { 4 | def solve(a: Double, b: Double, c: Double): Seq[Double] = ??? 5 | } 6 | -------------------------------------------------------------------------------- /src/main/scala/com/workshop/QuadraticJ.java: -------------------------------------------------------------------------------- 1 | package com.workshop; 2 | 3 | public class QuadraticJ { 4 | public static double[] square(double a, double b, double c) { 5 | double squareUrav[]; 6 | double d; 7 | 8 | d = b * b - 4 * a * c; 9 | 10 | if (d > 0) { 11 | squareUrav = new double[2]; 12 | squareUrav[0] = (-b - Math.sqrt(d)) / (2 * a); 13 | squareUrav[1] = (-b + Math.sqrt(d)) / (2 * a); 14 | return squareUrav; 15 | 16 | } else if (d == 0) { 17 | squareUrav = new double[1]; 18 | squareUrav[0] = -b / (2 * a); 19 | return squareUrav; 20 | 21 | } else { 22 | squareUrav = new double[0]; 23 | return squareUrav; 24 | 25 | } 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /src/test/scala/com/workshop/CollectionsTest.scala: -------------------------------------------------------------------------------- 1 | package com.workshop 2 | 3 | import com.workshop.Collections._ 4 | import org.specs2.mutable.Specification 5 | 6 | class CollectionsTest extends Specification { 7 | 8 | 9 | "doubleEachElement" should { 10 | "double each element" in { 11 | doubleEachElement(Seq(1, 2, 3)) must be_===(Seq(2, 4, 6)) 12 | } 13 | 14 | "onlySmaller" should { 15 | "keep only smaller integers" in { 16 | onlySmaller(Seq(1, 2, 3, 4, 5), 3) must be_===(Seq(1, 2)) 17 | } 18 | } 19 | 20 | "keepOddNumberOnly" should { 21 | "keep only odd numbers" in { 22 | keepOddNumberOnly(Seq(1, 2, 3, 4, 5)) must be_===(Seq(1, 3, 5)) 23 | } 24 | } 25 | 26 | "oddPositionsZeroBased" should { 27 | "take only elements in odd positions" in { 28 | oddPositionsZeroBased(Seq(0, 1, 2, 3, 4)) must be_===(Seq(1, 3)) 29 | } 30 | } 31 | 32 | "longest" should { 33 | "return the longest String" in { 34 | val long = "longggg" 35 | longest(Seq("a", "b", long, "short", "sd")) must be_===(long) 36 | } 37 | } 38 | 39 | "isAllUpper" should { 40 | "return true for all upper string" in { 41 | isAllUpper("CAPS") must beTrue 42 | } 43 | 44 | "return false if contains non capital letter" in { 45 | isAllUpper("CApS") must beFalse 46 | } 47 | } 48 | 49 | "reverse" should { 50 | "reverse a sequence" in { 51 | println(s"reverse(Seq(1,2,3,4)) = ${reverse(Seq(1, 2, 3, 4))}") 52 | reverse(Seq(1,2,3,4)) must be_===(Seq(4,3,2,1)) 53 | } 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /src/test/scala/com/workshop/DuplicatesRemoverTest.scala: -------------------------------------------------------------------------------- 1 | package com.workshop 2 | 3 | import org.specs2.mutable.Specification 4 | import org.specs2.specification.Scope 5 | 6 | class DuplicatesRemoverTest extends Specification { 7 | 8 | class Context extends Scope { 9 | val remover = new DuplicatesRemover 10 | val noDuplicates: Seq[Int] = 1 to 20 11 | val withDuplicates = noDuplicates ++ noDuplicates.reverse 12 | } 13 | 14 | "remove" should { 15 | "return same seq when no duplicates" in new Context { 16 | remover.remove(noDuplicates) must be_===(noDuplicates) 17 | } 18 | 19 | "remove the duplicates and keep the order" in new Context { 20 | remover.remove(withDuplicates) must be_===(noDuplicates) 21 | } 22 | 23 | "return empty seq for empty input" in new Context { 24 | remover.remove(Seq.empty) must be_===(Seq.empty) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/test/scala/com/workshop/ExpressionEvaluatorTest.scala: -------------------------------------------------------------------------------- 1 | package com.workshop 2 | 3 | import org.specs2.mutable.Specification 4 | import ExpressionEvaluator._ 5 | 6 | class ExpressionEvaluatorTest extends Specification { 7 | 8 | "eval" should { 9 | "evaluate constant" in { 10 | val n = 5 11 | evaluate(Const(n)) must be_===(n) 12 | } 13 | 14 | "evaluate Sum" in { 15 | val a = Const(2) 16 | val b = Const(3) 17 | evaluate(Sum(a, b)) must be_===(5) 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/test/scala/com/workshop/FibonacciTest.scala: -------------------------------------------------------------------------------- 1 | package com.workshop 2 | 3 | import org.scalacheck.Gen 4 | import org.specs2.ScalaCheck 5 | import org.specs2.mutable.Specification 6 | 7 | class FibonacciTest extends Specification with ScalaCheck { 8 | val fib: Fibonacci = new Fibonacci 9 | 10 | "negative input" should { 11 | "be invalid" in { 12 | fib.nth(-1) must throwAn[IllegalArgumentException] 13 | } 14 | } 15 | 16 | "0th" should { 17 | "be 0" in { 18 | fib.nth(0) must be_===(0) 19 | } 20 | } 21 | 22 | "1st" should { 23 | "be 1" in { 24 | fib.nth(1) must be_===(1) 25 | } 26 | } 27 | 28 | "2nd" should { 29 | "be 1" in { 30 | fib.nth(2) must be_===(1) 31 | } 32 | } 33 | 34 | "3rd" should { 35 | "be 2" in { 36 | fib.nth(3) must be_===(2) 37 | } 38 | } 39 | 40 | "fib(n)" should { 41 | "be equal to the sum of the results of last 2 elements" >> prop { n: Int => 42 | fib.nth(n) must be_===(fib.nth(n - 1) + fib.nth(n - 2)) 43 | }.setGen(Gen.choose[Int](2, 30)) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/scala/com/workshop/FizzBuzTest.scala: -------------------------------------------------------------------------------- 1 | package com.workshop 2 | 3 | import org.specs2.mutable.Specification 4 | import FizzBuz._ 5 | 6 | class FizzBuzTest extends Specification { 7 | "divisible by 3 and 5" should { 8 | "return FizzBuzz" in { 9 | eval(15) must be_===("FizzBuzz") 10 | } 11 | } 12 | 13 | "divisible by 3" should { 14 | "return Fizz" in { 15 | eval(3) must be_===("Fizz") 16 | } 17 | } 18 | 19 | "divisible by 5" should { 20 | "return FizzBuzz" in { 21 | eval(5) must be_===("Buzz") 22 | } 23 | } 24 | 25 | "non divisible by 3 or 5" should { 26 | "return itself" in { 27 | eval(1) must be_===("1") 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/scala/com/workshop/FrequencyMapTest.scala: -------------------------------------------------------------------------------- 1 | package com.workshop 2 | 3 | import org.specs2.mutable.Specification 4 | import org.specs2.specification.Scope 5 | 6 | import scala.util.Random 7 | 8 | class FrequencyMapTest extends Specification { 9 | 10 | class Context extends Scope { 11 | val frequency = new FrequencyMap 12 | 13 | val frequencies = Seq( 14 | 'a' -> Random.nextInt(1000), 15 | 'b' -> Random.nextInt(1000)) 16 | 17 | val dummyText: String = 18 | Random.shuffle( 19 | frequencies.flatMap { 20 | case (char, count) => Seq.fill(count)(char) 21 | }).mkString("") 22 | } 23 | 24 | "generate" should { 25 | "create a frequencies map" in new Context { 26 | frequency.generate(dummyText) must havePairs(frequencies: _*) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/test/scala/com/workshop/FunctionsCompositionTest.scala: -------------------------------------------------------------------------------- 1 | package com.workshop 2 | 3 | import com.workshop.FunctionsComposition._ 4 | import org.specs2.mutable.Specification 5 | 6 | 7 | class FunctionsCompositionTest extends Specification { 8 | "camelCase" should { 9 | "convert string to camleCase" in { 10 | camelCase("hello WORLD") must be_===("HelloWorld") 11 | } 12 | } 13 | 14 | 15 | private val epsilon = 0.01 16 | 17 | "addTaxThenDiscount" should { 18 | "apply the tax and discount first then shipping" in { 19 | calculatePriceThenShipping(100) must beCloseTo(106, epsilon) 20 | } 21 | } 22 | 23 | "doDiscountThenAddTax" should { 24 | "apply shipping cost first and then tax and discount" in { 25 | addShippingThenCalculatePrice(100) must beCloseTo(105.6, epsilon) 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/test/scala/com/workshop/FunctionsHigherOrderTest.scala: -------------------------------------------------------------------------------- 1 | package com.workshop 2 | 3 | import com.workshop.FunctionsHigherOrder._ 4 | import org.specs2.mutable.Specification 5 | 6 | class FunctionsHigherOrderTest extends Specification { 7 | "multiply" should { 8 | "return the mult of two integers" in { 9 | multiply(4, 5) must be_===(20) 10 | } 11 | } 12 | 13 | "sum" should { 14 | "return the sum of two integers" in { 15 | sum(4, 5) must be_===(9) 16 | } 17 | } 18 | 19 | "plusOne" should { 20 | "add 1 to a given number" in { 21 | plusOne(6) must be_===(7) 22 | } 23 | } 24 | 25 | "sumSeq" should { 26 | "sum all numbers" in { 27 | sumSeq(numbers) should be_===(10) 28 | } 29 | } 30 | 31 | 32 | "sumDoubles" should { 33 | "sum all doubles of the numbers" in { 34 | sumDoubles(numbers) should be_===(20) 35 | } 36 | } 37 | 38 | "sumSquares" should { 39 | "sum all squares of the numbers" in { 40 | sumSquares(numbers) should be_===(30) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/scala/com/workshop/IsIncreasingTest.scala: -------------------------------------------------------------------------------- 1 | package com.workshop 2 | 3 | import org.specs2.mutable.Specification 4 | 5 | class IsIncreasingTest extends Specification { 6 | val intSmaller = (a: Int, b: Int) => a < b 7 | val isSorted = new IsIncreasing[Int](intSmaller) 8 | 9 | "check" should { 10 | "be true for increasing seq" in { 11 | isSorted.check(Seq(1, 2, 3, 4)) must beTrue 12 | } 13 | 14 | "be false for non-sorted seq" in { 15 | isSorted.check(Seq(1, 2, 5, 3, 4)) must beFalse 16 | } 17 | 18 | "be false for reverse-sorted seq" in { 19 | isSorted.check(Seq(4, 3, 2, 1)) must beFalse 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/test/scala/com/workshop/LongestSubstringTest.scala: -------------------------------------------------------------------------------- 1 | package com.workshop 2 | 3 | import org.specs2.mutable.Specification 4 | 5 | class LongestSubstringTest extends Specification { 6 | "find" should { 7 | "find longest string with upper&lower case but not numbers" in { 8 | LongestSubstring.find("aV3rwerwr6Wix") must be_===("Wix") 9 | } 10 | } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /src/test/scala/com/workshop/PersonTitleTest.scala: -------------------------------------------------------------------------------- 1 | package com.workshop 2 | 3 | import com.workshop.PersonTitle._ 4 | import org.specs2.mutable.Specification 5 | 6 | class PersonTitleTest extends Specification { 7 | Seq(None, Some(Married), Some(Single)).foreach { status => 8 | "male" should { 9 | s"called Mr when status - $status" in { 10 | genderTitle(Male, status) must be_===("Mr") 11 | } 12 | } 13 | } 14 | 15 | "female" should { 16 | "called Miss when single" in { 17 | genderTitle(Female, Some(Single)) must be_===("Miss") 18 | } 19 | 20 | "called Mrs when married" in { 21 | genderTitle(Female, Some(Married)) must be_===("Mrs") 22 | } 23 | 24 | "called Ms when status is unknown" in { 25 | genderTitle(Female, None) must be_===("Ms") 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/test/scala/com/workshop/QuadraticTest.scala: -------------------------------------------------------------------------------- 1 | package com.workshop 2 | 3 | import org.specs2.mutable.Specification 4 | import Quadratic._ 5 | 6 | 7 | class QuadraticTest extends Specification { 8 | "solve" should { 9 | "have no solutions" in { 10 | solve(1, -2, 2) must beEmpty 11 | } 12 | 13 | "have one solutions" in { 14 | solve(1, 2, 1) must contain(exactly(-1d)) 15 | } 16 | 17 | "have multiple solutions" in { 18 | solve(1, 4, 3) must contain(exactly(-1d, -3d)) 19 | } 20 | } 21 | } 22 | --------------------------------------------------------------------------------