Q13. What are the different approaches in Scala to solve the following task?
Given numbers 1 to 6, and you need to extract out the odd numbers and double them, and finally print the result.
A13. There are 3 ways to solve the problem.
1) Loops (e.g. for, while, do-while)
2) Recursion
3) Combinators (e.g. map, foreach, filter, flatMap, foldLeft, etc)
Loop
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
package com.mytutorial.problem1 object LoopInScala extends App { val myList = List(1,2,3,4,5,6) for(x <- myList) { if(x % 2 != 0) { println(x * 2) } } } |
Recursion
:: means “Common method or object”. “::” in this expression is the method of the class List.
tail method returns a list consisting of all elements except the first.
x represents the “head”, which is the first element.
Nil represents an empty list.
x :: tail means x is the head (i.e. first element of the list), and tail is “all elements except the first”.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package com.mytutorial.problem1 object RecursionInScala extends App { val myList = List(1, 2, 3, 4, 5, 6) def printDoubledOddNums(listOfNums: List[Int]): Unit = listOfNums match { case Nil => case x :: tail => { if (x % 2 != 0) println(x * 2); printDoubledOddNums(tail) } } printDoubledOddNums(myList); } |
Functional Combinators
Some of the functional combinators in Scala are like “map“, which applies the supplied function to each element and return the result to a new list. The “foreach” functional combinator does the same things the “map”, but does not return a value. filter removes any element that returns false to the supplied predicate (e.g. x % 2 != 0). There are many other functional combinators like flatten, foldLeft, foldRight, zip, find, partition, dropWhile, etc.
1 2 3 4 5 6 7 8 9 10 11 12 |
package com.mytutorial.problem1 object FunctionalCombinatorInScala extends App { val myList = List(1, 2, 3, 4, 5, 6) myList.filter { x => x % 2 != 0} //returns a new immutable list with 1, 3, 5 .foreach { x => println( x * 2) } //prints 2, 6, 10 } |
Q14. Which approach among loops, iteration, and higher-order combinators will you favor in which scenario?
A14. As a general rule of thumb, favor “higher-order combinators” over the other two. Looping should only be considered in performance critical sections, and recursion should be considered in some edge cases where combinators cannot be used.
Combinators are not only much easier to read, but also promotes shorter code and fewer mistakes. Combinators allow you to combine small functions into big functions.
Q15. What will be the output of the following code snippet?
1 2 3 4 5 6 7 8 9 10 11 12 |
package com.mytutorial.problem1 object MapVsFlatMap extends App { val myList = List(List(1, 2), List(4, 5)) println(myList.map(x => x)) println(myList.flatMap(x => x)) } |
A15. The “flatMap” flattens the list of lists to a single list.
1 2 3 4 |
List(List(1, 2), List(4, 5)) List(1, 2, 4, 5) |
Q16. Can the following code snippet be re written with the “foldLeft” combinator?
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package com.mytutorial.problem1 object DoubleAndSumNumbers extends App { val myList = List(3, 4, 5); val result = myList.map { x => x * 2 } .reduce { (x, y) => x + y} println("result = " + result) } |
A16. Yes. The above code doubles each number in the “map” function and then “reduce” all the numbers i.e. (6, 8, 10) by adding each number to the next number:
Pass 1: 14, 10
Pass 2: 24
Using “foldLeft”
1 2 3 4 5 6 7 8 9 10 11 |
package com.mytutorial.problem1 object DoubleAndSumNumbers extends App { val myList = List(3, 4, 5); val result = myList.map(x => x * 2 ).foldLeft(0)(_ + _ ) println("result = " + result) } |
It starts with 0 as the initial result and then sums the result with the next doubled number.
Pass 1: 0 + 6
Pass 2: 6 + 8
Pass 3: 14 + 10
Q17. How will you go about combining two functions?
A17. You can use the functional combinators like “combine” and “andThen“. Here is an example that is used with a “Future” object, which is used for asynchronous (i.e. non blocking) processing.
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 |
package com.mytutorial.problem1 import scala.concurrent.Future import scala.util.Random import scala.concurrent.ExecutionContext.Implicits.global import scala.actors.threadpool.TimeUnit import scala.util.{Failure, Success} object FutureCombinator extends App { println("Future is an object holding a value") val f = Future { TimeUnit.SECONDS.sleep(5) 10 } f.map { x => println(x) }.andThen { case Success(x) => println("Successfully printed the number ") case Failure(x) => x.printStackTrace(); }. andThen { case Success(x) => println("Wow I am next") case Failure(x) => x.printStackTrace(); } println("Future become available asynchronously") TimeUnit.SECONDS.sleep(10) //keep the JVM alive } |
Output
1 2 3 4 5 6 7 |
Future is an object holding a value Future become available asynchronously 10 Successfully printed the number Wow I am next |
Q18. How will you split the following list into odd and even numbers>
1 2 3 |
val numbers = List(1, 2, 3, 4, 5, 6) |
A18. You can use the “partition” function.
1 2 3 4 5 6 7 8 9 10 11 12 |
package com.mytutorial.problem1 object PartitionScala extends App { val numbers = List(1, 2, 3, 4, 5, 6) val result = numbers.partition { x => x % 2 != 0 } println(result) // (List(1, 3, 5),List(2, 4, 6)) } |