Scala Introduction
Learning Objectives
- Provide more details of scala language
Basic Gramma
Start using Scala
After installed scala, you can type scala
in command line and get result as follow:
$ scala
Welcome to Scala version 2.11.7
Type in expressions to have them evaluated.
Type :help for more information.
scala> println("Hello, World!")
Hello, World!
The synopsis of a varialbe is:
scala> val i:String = "abc"
i: String = abc
- val means it it is immutable variable, you can use “var” to define a mutable variable
- i is the name of this variable
- String is the type of this string, it can be omitted here
- “abc” is the value of this variable
Define a function:
scala> def foo(v0:Int, v1:Int):Int = {
| println(v0 max v1)
| v0 + v1
| }
foo: (v0: Int, v1: Int)Int
scala> foo(1, 2)
2
res0: Int = 3
Define a class:
scala> class Foo(a:String, b:Int) {
| def length = a.length
| }
defined class Foo
scala> val foo:Foo = new Foo("Hello, World!", 3)
foo: Foo = Foo@6438a396
scala> println(foo.length)
13
Define a case class:
scala> case class Foo(a:String, b:Int)
defined class Foo
scala> val foo:Foo = Foo(a = "Hello, World!", b = 3)
foo: Foo = Foo(Hello, World!,3)
scala> println(foo.a)
Hello, World!
Differences between case class and class
Define an Object
scala> object Foo {
| def greeting() {
| println("Greeting from Foo")
| }
| }
defined object Foo
scala> Foo.greeting()
Greeting from Foo
Functions/variables in Object is similar to the static function and variable in Java.
What is ought to be highligted is the use of “apply”. SomeObject.apply(v:Int) equals SomeObject(v:Int)
scala> :paste
// Entering paste mode (ctrl-D to finish)
case class Foo(a:String, b:Int)
object Foo {
def apply(a:String): Foo =
Foo(a, a.length)
}
// Exiting paste mode, now interpreting.
defined class Foo
defined object Foo
scala> val foo = Foo("Hello, World!")
foo: Foo = Foo(Hello, World!,13)
Finally, we should know the usage of code block.
We can create a code block anywhere, and the last line is the result of this block.
For example,
def foo(i:Int) = {
println(s"value: $i")
i * 2
}
val newList = List[Int](1, 2, 3).map(i => foo(i))
We can use the follow lines instead:
val newList = List[Int](1, 2, 3).map(i => {
println(s"value: $i")
i * 2
})
A better practice here is:
val newList = List[Int](1, 2, 3).map{i =>
println(s"value: $i")
i * 2
}
Case Study of some Common Types
Option, Some, None
We can use null
in Scala as null pointer, but it is not recommended. We are supposed to use Option[SomeType]
to indicate this variable is optional.
We can assueme every variable without Option
are not null pointer if we are not calling java code. This help us reduce a lot of code.
If we wanna to get a variable with Option, here are two method
val oi = Option(1)
val i = oi match {
case Some(ri) => ri
case None => -1
}
println(i)
Besides, we can also use method “isDefined/isEmpty”.
val oi = Option(1)
if(oi.isDefined) {
println(s"oi: ${oi.get}")
} else {
println("oi is empty")
}
What should be highlighted is Option(null)
returns None
,
but Some(null)
is Some(null)
which is not equals None
.
match
is a useful reserved words, we can use it in various of situations
Firstly, we can use it as “switch” & “case” in some other programming languages.
true match {
case true => println("true")
case false => println("false")
}
Secondly, we can find it by the type of the data,
case class A(i:Int,j:Double)
case class B(a:A, k:Double)
val data = B(A(1,2.0),3.14)
data match {
case B(A(_,v),_) =>
println(s"required value: $v")
case _ =>
println("match failed")
}
Given a case class B, but we only wish to retrievee the value B.a.j, we can use “_” as placeholder.
Common methods in List, Array, Set, and so on
In scala, we always transfer the List( Array, Set, Map etc.) from one status to another status. the methods of
-
toList, toArray, toSet – convert each other
-
par – Parallelize List, Array and Map, the result of
Seq[Int]().par
isParSeq[Int]
, you will able to process each element in parallel when you are using foreach, map etc., and unable to call “sort” before you are using “toList”. -
distinct – Removes duplicate elements
-
foreach – Process each element and return nothing
List[Int](1,2,3).foreach{ i =>
println(i)
}
It wil print 1, 2, 3 in order
List[Int](1,2,3).par.foreach{ i =>
println(i)
}
It will print 1, 2, 3, but the order is not guaranteed.
- map – Process each element and construct a List using return value
List[Int](1,2,3).map{ i => i + 1}
It will return List[Int](2,3,4)
The result of List[A]().map(some-oper-return-type-B)
is List[B]
, while the result of Array[A]().map
map is Array[B]
.
- flatten – The flatten method takes a list of lists and flattens it out to a single list:
scala> List[List[Int]](List(1,2),List(3,4)).flatten
res1: List[Int] = List(1, 2, 3, 4)
scala> List[Option[Integer]](Some(1),Some[Integer](null),Some(2),None,Some(3)).flatten
res2: List[Integer] = List(1, null, 2, 3)
-
flatMap – The flatMap is similar to map, but it takes a function returning a list of elements as its right operand. It applies the function to each list element and returns the concatenation of all function results. The result equals to map + flatten
-
collect – The iterator obtained from applying the partial function to every element in it for which it is defined and collecting the results.
scala> List(1,2,3.4,"str") collect {
| case i:Int => (i * 2).toString
| case f:Double => f.toString
| }
res0: List[String] = List(2, 4, 3.4)
The function match elements in Int and Double, process them and return the value, but ignore string elements.
- filter – Filter this list
scala> List(1,2,3).filter(_ % 2 == 0)
res1: List[Int] = List(2)
- filterNot – Similar to filter
scala> List(1,2,3).filterNot(_ % 2 == 0)
res2: List[Int] = List(1, 3)
- forall – Return true if All elements are return true by the partial function. It will immediately return once one element returns false, and ignore the rest elements.
scala> List(2,1,0,-1).forall{ i =>
| val res = i > 0
| println(s"$i > 0? $res")
| res
| }
2 > 0? true
1 > 0? true
0 > 0? false
res0: Boolean = false
- exists – Return true if there are at least One element returns true.
scala> List(2,1,0,-1).exists{ i =>
| val res = i <= 0
| println(s"$i <= 0? $res")
| res
| }
2 <= 0? false
1 <= 0? false
0 <= 0? true
res2: Boolean = true
- find – Return the first element returns true by the partial function. Return None if no elemet found.
scala> List(2,1,0,-1).find{ i =>
| val res = i <= 0
| println(s"$i <= 0? $res")
| res
| }
2 <= 0? false
1 <= 0? false
0 <= 0? true
res3: Option[Int] = Some(0)
- sortWith – sort the elements
scala> List(1,3,2).sortWith((leftOne,rightOne) => leftOne > rightOne)
res5: List[Int] = List(3, 2, 1)
- zipWithIndex –
List("a","b").zipWithIndex.foreach{ kv:(String,Int) => println(s"k:${kv._1}, v:${kv._2}")}
It will rebuild a List with index
k:a, v:0
k:b, v:1
- for - Scala’s keyword
for
can be used in various of situations.
Basically,
for{
i <- List(1,2,3)
} yield (i,i+1)
It equals:
List(1,2,3).map(i => (i, i+1))
Besides,
for{
i <- List(1,2,3)
j <- List(4,5,6)
} yield (i,j)
We will get the cartesian product of List(1,2,3)
and List(4,5,6)
: List((1,4), (1,5), (1,6), (2,4), (2,5), (2,6), (3,4), (3,5), (3,6))
We can add a filter in condition:
for{
i <- List(1,2,3)
if i != 1
j <- List(4,5,6)
if i * j % 2 == 1
} yield (i,j)
the result is : List((3,5))
Another usage of for is as follow:
Let’s define variables as follow:
val a = Some(1)
val b = Some(2)
val c = Some(3)
We can execute like this:
for {
i <- a
j <- b
k <- c
r <- {
println(s"i: $i, j:$j, k:$k")
Some(i * j * k)
}
} yield r
The response is:
i: 1, j:1, k:3
res9: Option[Int] = Some(6)
Let’s define b as None:
scala> val b:Option[Int] = None
b: Option[Int] = None
scala> for {
| i <- a
| j <- b
| k <- c
| r <- {
| println(s"i: $i, j:$j, k:$k")
| Some(i * j * k)
| }
| } yield r
res14: Option[Int] = None
- while - Similar to while in java
var i = 0
while ({
i = i + 1
i < 1000
}){
// body of while
println(s"i: $i")
}
-
to, until — (1 to 10) will generate a Seq, with the content of (1,2,3,4…10), (0 until 10) will generate a sequence from 0 to 9. With some test, (0 until 1000).map(xxx) appears to be slower than
var i=0; while( i < 1000) { i += 1; sth. else}
, but if the body of map is pretty heavy, this cost can be ignored. -
headOption - Get the head of one list, return None if this list is empty
-
head - Get the head of one list, throw exception if this list is empty
-
take – Get first at most N elements. (from left to right)
scala> List(1,2,3).take(2)
res0: List[Int] = List(1, 2)
scala> List(1,2).take(3)
res1: List[Int] = List(1, 2)
- drop – Drop first at most N elements.
scala> List(1,2,3).drop(2)
res2: List[Int] = List(3)
scala> List(1,2).drop(3)
res3: List[Int] = List()
dropRight
will drop elements from right to left.
- slice – Return list in
[start-offset, end-offset)
scala> List(1,2,3).slice(1,2)
res7: List[Int] = List(2)
scala> List(1,2,3).slice(2,2)
res8: List[Int] = List()
val offset = 1
val size = 3
List(1,2,3,4,5).slice(offset, size + offset)
If the end-offset is greater than the length of this list, it will not throw exception.
- splitAt – Split this list into two from offset i
scala> List(1,2,3).splitAt(1)
res10: (List[Int], List[Int]) = (List(1),List(2, 3))
- groupBy – Partitions a list into a map of collections according to a discriminator function
scala> List(1,2,3).groupBy(i => if(i % 2 == 0) "even" else "odd" )
res11: scala.collection.immutable.Map[String,List[Int]] = Map(odd -> List(1, 3), even -> List(2))
- partition – Splits a list into a pair of collections; one with elements that satisfy the predicate, the other with elements that do not, giving the pair of collections (xs filter p, xs.filterNot p).
scala> List(1,2,3).partition(_ % 2 == 0)
res12: (List[Int], List[Int]) = (List(2),List(1, 3))
- grouped – The grouped method chunks its elements into increments.
scala> List(1,2,3,4,5).grouped(2)
res13: Iterator[List[Int]] = Iterator(List(1, 2), List(3, 4), List(5))
You can visit this PDF for an official guide.
We also highly recomended to read the book Programming in Scala for more detail instruction.