Lambda Expression in Scala
Lambda Expression refers to an expression that uses an anonymous function instead of variable or value. Lambda expressions are more convenient when we have a simple function to be used in one place. These expressions are faster and more expressive than defining a whole function. We can make our lambda expressions reusable for any kind of transformations. It can iterate over a collection of objects and perform some kind of transformation to them.
Syntax:
val lambda_exp = (variable:Type) => Transformation_Expression
Example:
// lambda expression to find double of x
val ex = (x:Int) => x + x
Example :
// Scala program to show
// working of lambda expressio
// Creating object
object GfG
// Main method
def main(args:Array[String])
{
// lambda expression
val ex1 = (x:Int) => x + 2
// with multiple parameters
val ex2 = (x:Int, y:Int) => x * y
println(ex1(7))
println(ex2(2, 3))
}
}
Output:
9
6
// Scala program to apply
// transformation on collection
// Creating object
object GfG
{
// Main method
def main(args:Array[String])
{
// list of numbers
val l1 = List(1, 1, 2, 3, 5, 8)
val l2 = List(13, 21, 34)
// reusable lambda
val func = (x:Int) => x * x
// squaring each element of the lists
val res1 = l1.map( func )
val res2 = l2.map( func )
println(res1)
println(res2)
}
}
Output:
List(1, 1, 4, 9, 25, 64)
List(169, 441, 1156)
FUNCTIONAL DATA STRUCTURES
In this chapter you’ll switch gears to begin a fun and interesting part of the Scala language: Scala collections which broadly support two categories of data structures—immutable and mutable.
A mutable collection can be updated or extended in place. This means you can change, add, or remove elements of a collection as a side effect. Immutable collections, by contrast, never change.
To understand and benefit from Scala collections, you need to know two concepts: type parameterization and higher-order functions. Type parameterization allows you to create types that take another type as a parameter (similar to Java generics). Higher-order functions let you create functions that take other functions as parameters. These two concepts allow you to create generic and reusable components, like Scala collections.
The Scala collection is one of Scala’s most powerful features. The library implements all the common data structures you need, making it essential for every Scala developer. A recent addition to the collections library is parallel collections. Scala parallel collections allow you to solve data parallelism problems in Scala with ease. You’ll see how the Scala parallel collections library helps when working with large datasets, so buckle up! This will be a fun and exciting ride.
ListMap in Scala
Immutable maps Implemented by using a list-based data structure. The Scala List class holds a sequenced, linear list of items. We must import scala.collection.mutable.ListMap for ListMap. ListMap collection used only for a small number of elements.
Syntax:
var listMapName = ListMap("k1"->"v1", "k2"->"v2", "k3"->"v3", ...)
Here, k is key and v is value.
Creating an ListMap:
In below code we can see a ListMap is created with values.
// Scala program to create or print ListMap
import scala.collection.immutable.ListMap
// Creating object
object Geeks
{
// Main method
def main(args: Array[String])
{
// Creating ListMap with values
var listMap = ListMap("C"->"Csharp", "S"->"Scala", "J"->"Java")
// Printing ListMap
println(listMap)
}
}
Output:
Map(C -> Csharp, S -> Scala, J -> Java)
Adding and accessing elements :
A ListMap is created, add elements and access elements also performed.
// Scala program to Adding and Accessing Elements ListMap
import scala.collection.mutable.ListMap
// Creating object
object Geeks
{
// Main method
def main(args: Array[String])
{
// Creating ListMap
var listMap = ListMap("C"->"Csharp", "S"->"Scala", "J"->"Java")
// Iterating elements
listMap.foreach
{
case (key, value) => println (key + " -> " + value)
}
// Accessing value by using key
println(listMap("S"))
// Adding element
var ListMap2 = listMap + ("P"->"Perl")
ListMap2.foreach
{
case (key, value) => println (key + " -> " + value)
}
}
}
Output:
J -> Java
C -> Csharp
S -> Scala
Scala
P -> Perl
C -> Csharp
J -> Java
Recommended by LinkedIn
S -> Scala
Removing an element from ListMap :
A ListMap is created than removing an element is performed using – sign. Below is the example to removing an element from ListMap.
// Scala program to removing Element from ListMap
import scala.collection.mutable.ListMap
// Creating object
object Geeks
{
// Main method
def main(args: Array[String])
{
// Creating ListMap
var listMap = ListMap("C"->"Csharp", "S"->"Scala", "J"->"Java")
// Iterating elements
listMap.foreach
{
case (key, value) => println (key + " -> " + value)
}
// Removing an element
listMap -= "C"
println("After Removing")
listMap.foreach
{
case (key, value) => println (key + " -> " + value)
}
}
}
Output:
J -> Java
C -> Csharp
S -> Scala
After Removing
J -> Java
S -> Scala
Creating an empty ListMap:
An empty ListMap is created either by calling its constructor or using ListMap.empty method.
// Scala program to Create an empty ListMap
import scala.collection.mutable.ListMap
// Creating object
object Geeks
{
// Main method
def main(args: Array[String])
{
// Creating an empty list map by calling constructor
var ListMap1 = new ListMap()
// Creating an empty list map by using .empty method
var ListMap2 = ListMap.empty
// Printing empty ListMap
println(emptyListMap1)
println(emptyListMap2)
}
}
Output:
Map()
Map()
Scala | Lazy Evaluation
Lazy evaluation or call-by-need is a evaluation strategy where an expression isn’t evaluated until its first use i.e to postpone the evaluation till its demanded. Functional programming languages like Haskell use this strategy extensively. C, C++ are called strict languages who evaluate the expression as soon as it’s declared. Then there are languages like Scala who are strict by default but can be lazy if specified explicitly i.e. of mixed type.
Let’s see an example in Scala:
Without lazy:
val geeks = List(1, 2, 3, 4, 5)
val output = geeks.map(l=> l*2)
println(output)
The value of output is calculated as soon as the operation is applied on it.
With lazy:
val geeks = List(1, 2, 3, 4, 5)
lazy val output2 = geeks.map(l=> l*2)
println(output2)
The value isn’t calculated till we use output2 that’s till println(output2).
Why lazy evaluation?
In the example what if we never use the output value? We wasted our map operation (CPU computations) which can be very costly when we write more complex and bigger code. Here lazy evaluation helps us in optimizing the process by evaluating the expression only when it’s needed and avoiding unnecessary overhead.
Pros:
· Optimizes the computation process. Spark a big data computation engine uses this technique at it’s core.
· Lazy evaluation can help us to resolve circular dependencies.
· Gives access to infinite data structure.
· Allows modularity of code into parts.
· The programmer lose control over the sequence their code is executed as some expressions are evaluated and others aren’t depending on the need.
Cons:
· Finding bugs can be tricky as programmer has no control over program execution.
· Can increase space complexity as all the instructions(operations) have to stored.
· Harder to code in contrast with conventional approach.
Lazy function values in Higher Order functions
We created one Higer Order function example earlier and cached the function value. However, we learned that Scala evaluates the function immediately as we cache it. In that scenario, even if we do not use the cached value, the function gets evaluated at the time of caching.
The following example uses a lazy val to cache the function value. This simple technique makes the function value lazy, and instead of immediate evaluation, the function value is evaluated once at the time of its first use.
//define the function
def twice(f: => Int) = {
lazy val i = f
println("We did not use i yet")
i + i
}
//call the function
twice(factorial(15) / factorial(11))
/* Output:-
We did not use i yet
Starting Factorial for 15
Starting Factorial for 11
res1: Int = 100
*/
Benefits of lazy evaluation
Are you wondering about the benefits? Why would you want to use the lazy evaluations? The laziness is mostly used to create data structures to handle large data volumes efficiently. Apache Spark libraries are the most common examples. If you know Spark RDD, they implement all transformations as lazy operations. Let's take an example to understand the benefits. I have a log file. There are different types of entries. Every line in the file has a record type of notice, error or info. We want to extract first two error messages.
Here is a simple Scala code to do that.
import scala.io._
val s = Source.fromFile("error_log") .getLines() .toList.filter(_.contains("[error]"))
.take(2) s foreach println
The above code gives me what I was looking for. However, did you notice that how this code works? We open the file, read all the lines and convert it to a list. Then we filter for all the lines that contain error. Finally, we take only first two of those lines. Assuming that my file is large, maybe few GBs, Is this an efficient method? I mean, I can create a simple loop. Read the file line by line until I get two error lines, and stop as soon as I get two error messages. That could be more efficient. Isn't it. I may get first two error messages at the beginning and avoid time and memory to process rest of the data. However, the example shown above is reading all the lines. It happens because the List is a strict data structure. When we call toList function, It evaluates immediately. So, we get all the lines from the file even before we apply a filter to it. Scala gives you a lazy alternative to lists. They call it Stream.
👏