Scala Cheat Sheet
Scala Cheat Sheet
Scala Cheat Sheet
ipynb
https://github.com/nihitx/apache-spark
Table of Contents
Scala Cheat Sheet
Scala essentials
o Display and Strings
o Blocks and expressions
o Method definitions
o Conditional
o Pattern matching
o Exceptions
o Parametric type
Object oriented programming
o General hierarchy of classes / traits / objects
o object
o class
Arrays
o Declaration of array
o Access to the elements
o Iteration on the elements of an array
o Creating an array by transformation
o for/yield
o filter
o map
o sortWith
o reduce
o Arrays with a variable size
Main collections
o Principles
o Tuples
o Map
o Map implementations
o Option
o Either
o Lists
o Set
o Set Implementations
o Vector
o Stack
o Queue
o Range
o Streams
Collections in detail
o Traversable
o Iterable
o Seq
o Usual operations
o Add / remove an element
o Update
o Assemblist operations
o Cast
o Operations of Iterables
o Selection and cutting
o Transformation of a collection
o Operations of Seq
o Parallel collections
o par
o Lazy view
Object oriented programming
o Class statement
o public, private and protected access
o public
o private
o protected
o Refine the scope of access
o Getters and Setters
o Constructors
o Nested Classes
o Anonymous Classes
o Type and this
o Transtyping
o Object
o Singleton Object
o Any Methods
o Apply and Unapply
o case class
o Inheritance
o extends
o Constructor and super-class
o override
o Abstract class
o final
o Implicit conversions
o Implicit class
o implicit conversions method
o Trait
Scala essentials
In [1]:
var a = 5 // variable
var b: Int = 5 // variable with explicit typing
val zero = 0 // constant
Out[1]:
0
Method definitions
In [11]:
def a = 1
Out[11]:
1
In [12]:
def sum(v1: Int, v2: Int) = v1 + v2
def sum1(v1: Int, v2: Int): Int = v1 + v2 // to indicate the type of return
Out[12]:
sum: (v1: Int, v2: Int)Int
sum1: (v1: Int, v2: Int)Int
In [13]:
def zeroOrx(x: Int = 0) = x // parameter with default value
s"${zeroOrx()}, ${zeroOrx(100)}"
Out[13]:
0, 100
In [14]:
def mean(x: Int*) = x.sum / x.size
mean(1, 2, 3, 4, 5)
Out[14]:
3
In [15]:
// procedure (Unit): possibility to remove the "="
def unit() {println("Unit")}
Unit
Out[15]:
null
In [16]:
val flagColor: (String, String, String) => String = (c1, c2, c3) => {c1 + " " + c2 + " " + c3}
// ou
val flagColor1 = (c1: String, c2: String, c3: String) => {c1 + " " + c2 + " " + c3}
Out[16]:
<function3>
Conditional
In [17]:
if (scala.util.Random.nextFloat > 0.5) "win" else "loose"
Out[17]:
win
Pattern matching
In [18]:
scala.util.Random.nextInt match {
case 0 => println("zero")
case a if a > 0 => println("positive")
case a if a < 0 => println("negative")
case _ => println("end")
}
negative
Exceptions
In [19]:
throw new Exception("...")
java.lang.Exception: ...
... 44 elided
In [20]:
try {
1 / 0
} catch {
case ex: ArithmeticException => ex.printStackTrace()
case ex: Exception => ex.printStackTrace()
case _ => println("Nothing")
}
java.lang.ArithmeticException: / by zero
at $line46.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$
$iw.liftedTree1$1(<console>:91)
at $line46.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$
$iw.<init>(<console>:90)
at $line46.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$
$iw.<init>(<console>:101)
at $line46.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw.<init>(<console>:103)
at $line46.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw.<init>(<console>:105)
at $line46.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw.<init>(<console>:107)
at $line46.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw.<init>(<console>:109)
at $line46.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw.<init>(<console>:111)
at $line46.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw.<init>(<console>:113)
at $line46.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw.<init>(<console>:115)
at $line46.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw.<init>(<console>:117)
at $line46.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw.<init>(<console>:119)
at $line46.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw.<init>(<console>:121)
at $line46.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw.<init>(<console>:123)
at $line46.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw.<init>(<console>:125)
at $line46.$read$$iw$$iw$$iw$$iw$$iw$$iw.<init>(<console>:127)
at $line46.$read$$iw$$iw$$iw$$iw$$iw.<init>(<console>:129)
at $line46.$read$$iw$$iw$$iw$$iw.<init>(<console>:131)
at $line46.$read$$iw$$iw$$iw.<init>(<console>:133)
at $line46.$read$$iw$$iw.<init>(<console>:135)
at $line46.$read$$iw.<init>(<console>:137)
at $line46.$read.<init>(<console>:139)
at $line46.$read$.<init>(<console>:143)
at $line46.$read$.<clinit>(<console>)
at $line46.$eval$.$print$lzycompute(<console>:7)
at $line46.$eval$.$print(<console>:6)
at $line46.$eval.$print(<console>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:793)
at scala.tools.nsc.interpreter.IMain$Request.loadAndRun(IMain.scala:1054)
at scala.tools.nsc.interpreter.IMain$WrappedRequest$$anonfun$loadAndRunReq$1.apply(IMain.scala:645)
at scala.tools.nsc.interpreter.IMain$WrappedRequest$$anonfun$loadAndRunReq$1.apply(IMain.scala:644)
at scala.reflect.internal.util.ScalaClassLoader$class.asContext(ScalaClassLoader.scala:31)
at scala.reflect.internal.util.AbstractFileClassLoader.asContext(AbstractFileClassLoader.scala:19)
at scala.tools.nsc.interpreter.IMain$WrappedRequest.loadAndRunReq(IMain.scala:644)
at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:576)
at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:572)
at com.twosigma.beakerx.scala.evaluator.ScalaEvaluatorGlue.evaluate(ScalaEvaluatorGlue.scala:121)
at com.twosigma.beakerx.scala.evaluator.ScalaCodeRunner.call(ScalaCodeRunner.java:46)
at com.twosigma.beakerx.scala.evaluator.ScalaCodeRunner.call(ScalaCodeRunner.java:29)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Out[20]:
()
Parametric type
In [21]:
def hey[T](x: T) = "Hey " + x.toString
// equivalent to
def hey1(x: Any) = "Hey " + x.toString
hey[String]("Joe") + " " + hey1(122)
Out[21]:
Hey Joe Hey 122
In [22]:
import Ordering.Implicits._
Out[22]:
scala.math.Ordering$Implicits$@7d267ecc
In [23]:
def greaterThan[T: Ordering](x: T, y: T): Boolean = x > y
moreThan(1, 3)
<console>:95: error: not found: value moreThan
moreThan(1, 3)
^
In [24]:
type ArrInt = Array[Int] // type alias
Out[24]:
defined type alias ArrInt
class
In [26]:
class Money(startMoney: Int) {
def this() = this(0) // builder
private var contain: Int = 0
def +=(money: Int) = contain += money
def empty = {val solde = contain; contain = 0; solde}
def howMany = contain
override def toString = contain.toString
}
object Money {
// method and static variables
def foo = "bar"
}
Out[26]:
$line55.$read$$iw$$iw$Money$@2c10b763
In [27]:
val m = new Money()
m += 10
m.empty
Out[27]:
10
In [28]:
Money.foo
Out[28]:
bar
Arrays
Declaration of array
In [29]:
val ints = new Array[Int](30) // empty array
val ints2 = Array[Int](1, 2, 3, 4) // array with init values
Out[29]:
[1, 2, 3, 4]
In [30]:
val matrix4x9 = Array.ofDim[Double](4, 9)
Out[30]:
[[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]]
filter
In [41]:
Array(1, 2, 3, 4, 5).filter(x => x % 2 == 0)
Out[41]:
[2, 4]
map
In [42]:
Array(1, 2, 3, 4, 5).map(x => x*x)
Out[42]:
[1, 4, 9, 16, 25]
sortWith
In [43]:
Array(3, 6, 2, 0, 8, 5).sortWith((e1, e2) => e1 < e2)
Out[43]:
[0, 2, 3, 5, 6, 8]
reduce
In [44]:
Array(1, 2, 3, 4, 5).reduce((e1, e2) => e1+e2) // équivalent à Array(1, 2, 3, 4, 5).sum
Out[44]:
15
Tuples
Storage of values
In [52]:
val oneAndTwo = (1, 2)
val oneAndTwo1 = Tuple2(1, 2) // There are tuples that range from 1 element to 22
val oneAndTwo2 = 1 -> 2
Out[52]:
(1,2)
In [53]:
s"${oneAndTwo._1}, ${oneAndTwo._2}"
Out[53]:
1, 2
In [54]:
val (a, b, _, d) = (1, 2, 3, 4) // "_" for a meaningless value
s"$a $b $d"
Out[54]:
1 2 4
Map
Key-value couple
In [55]:
val map = Map(("one", 1), ("two", 2))
val map1 = Map("one" -> 1, "two" -> 2)
In [56]:
map("two")
Out[56]:
2
In [57]:
map.getOrElse("three", -1)
Out[57]:
-1
In [58]:
val mutableMap = scala.collection.mutable.Map[String, Int]()
mutableMap("three") = 3
mutableMap += (("one" -> 1), ("two" -> 2))
mutableMap -= "one"
mutableMap += "five" -> 5
Out[58]:
Map(three -> 3, five -> 5, two -> 2)
In [59]:
val reverseMap = for((key, value) <- mutableMap) yield (value, key)
// or
mutableMap.map(element => (element._2, element._1))
Out[59]:
Map(2 -> two, 5 -> five, 3 -> three)
In [60]:
s"keys: ${mutableMap.keys} values: ${mutableMap.values}"
Out[60]:
keys: Set(three, five, two) values: HashMap(3, 5, 2)
In [61]:
mutableMap.partition(_._2 % 2 == 0) // cut a Map according to a predicate
Out[61]:
(Map(two -> 2),Map(three -> 3, five -> 5))
Map implementations
Immutable
In [62]:
import scala.collection.immutable
immutable.HashMap[AnyVal, AnyVal]()
immutable.IntMap[AnyVal]()
immutable.ListMap[AnyVal, AnyVal]()
immutable.LongMap[AnyVal]()
immutable.SortedMap[Int, AnyVal]()
immutable.TreeMap[Int, AnyVal]()
Mutable
In [63]:
import scala.collection.mutable
mutable.HashMap[AnyVal, AnyVal]()
mutable.LinkedHashMap[AnyVal, AnyVal]()
mutable.ListMap[AnyVal, AnyVal]()
mutable.OpenHashMap[AnyVal, AnyVal]()
mutable.WeakHashMap[AnyVal, AnyVal]()
Out[63]:
Map()
Option
Presence (Some) or not (None) of value
In [64]:
val emptyOpt: Option[Int] = None
Out[64]:
None
In [65]:
val fullOpt: Option[Int] = Some(42)
Out[65]:
Some(42)
In [66]:
emptyOpt match {
case Some(value) => println(value)
case None => println("Empty")
}
Empty
In [67]:
fullOpt.get
Out[67]:
42
In [68]:
emptyOpt.isEmpty
Out[68]:
true
Either
Choice between 2 return values
In [69]:
def divide(a: Double, b: Double): Either[String, Double] = {
if (b==0.0) Left("Division by zero") else Right(a/b)
}
divide(4, 0)
Out[69]:
Left(Division by zero)
In [70]:
divide(4, 2)
Out[70]:
Right(2.0)
Lists
Object sequence defined in recursive ways
In [71]:
val list = List(1, 2, 3, 4)
Out[71]:
[[1, 2, 3, 4]]
In [72]:
list.head
Out[72]:
1
In [73]:
list.tail
Out[73]:
[[2, 3, 4]]
In [74]:
list.tail.tail.head
Out[74]:
3
In [75]:
list.tail.tail.tail.tail
Out[75]:
[[]]
In [76]:
Nil
Out[76]:
[[]]
In [77]:
1 :: List(2, 3, 4)
Out[77]:
[[1, 2, 3, 4]]
In [78]:
1 :: 2 :: 3 :: 4 :: Nil
Out[78]:
[[1, 2, 3, 4]]
In [79]:
list(3)
Out[79]:
4
In [80]:
def lCount(lst: List[Int], func: (Int) => Boolean): Int = {
if (lst == Nil) 0 else
(if (func(lst.head)) 1 else 0) + lCount(lst.tail, func)
}
lCount(List(1, 2, 3, 4), _ % 2 == 0)
Out[80]:
2
Set
Non-ordered collection with each element present only once
In [81]:
val set = Set(1, 2, 3, 4)
set + 3
Out[81]:
Set(1, 2, 3, 4)
In [82]:
set contains 1
// or
set(1)
Out[82]:
true
Set Implementations
Immutable
In [83]:
import scala.collection.immutable
immutable.BitSet()
immutable.HashSet[AnyVal]()
immutable.ListSet[AnyVal]()
immutable.TreeSet[Int]()
immutable.SortedSet[Int]()
Out[83]:
TreeSet()
Mutable
In [84]:
import scala.collection.mutable
mutable.BitSet()
mutable.HashSet[AnyVal]()
mutable.LinkedHashSet[AnyVal]()
mutable.TreeSet[Int]()
Out[84]:
TreeSet()
Vector
Collection with acceptable performance in access and update.
In [85]:
val v = Vector(1, 2, 3, 4)
Out[85]:
[[1, 2, 3, 4]]
In [86]:
v :+ 5 :+ 6
Out[86]:
[[1, 2, 3, 4, 5, 6]]
In [87]:
v(1)
Out[87]:
2
In [88]:
v updated(2, 10)
Out[88]:
[[1, 2, 10, 4]]
In [89]:
0 +: v
Out[89]:
[[0, 1, 2, 3, 4]]
Stack
LIFO
In [90]:
val s = scala.collection.immutable.Stack[Int](1, 2, 3, 4)
Out[90]:
[[1, 2, 3, 4]]
In [91]:
s push(29) push(98)
Out[91]:
[[98, 29, 1, 2, 3, 4]]
In [92]:
s top
Out[92]:
1
In [93]:
s pop // Remove the first element from the stack
Out[93]:
[[2, 3, 4]]
Queue
FIFO
In [94]:
val q = scala.collection.immutable.Queue[Int](1, 2, 3, 4)
Out[94]:
[[1, 2, 3, 4]]
In [95]:
q enqueue(98) enqueue(76)
Out[95]:
[[1, 2, 3, 4, 98, 76]]
In [96]:
q dequeue
Out[96]:
(1,Queue(2, 3, 4))
Range
In [97]:
1 to 10
Out[97]:
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]
In [98]:
1 until 10
Out[98]:
[[1, 2, 3, 4, 5, 6, 7, 8, 9]]
In [99]:
0 to 100 by 5
Out[99]:
[[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100]]
Streams
Lazy List, only the first occurrence is evaluated
In [100]:
val s = 1 #:: 2 #:: 3 #:: Stream.empty
Out[100]:
[[1, 2, 3]]
In [101]:
Stream(1, 2, 3)
Out[101]:
[[1, 2, 3]]
In [102]:
def fibonacci(a: Int, b: Int): Stream[Int] = a #:: fibonacci(b, a + b)
fibonacci(0, 1).take(10).force
Out[102]:
[[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]]
Collections in detail
Traversable
Traversables collections implement foreach, which allows the collection to be browsed by calling a function.
In [103]:
'a' to 'z' foreach print
abcdefghijklmnopqrstuvwxyz
Iterable
Crossover collections implement iterator, which allows access to each element without modification.
In [104]:
val ite = ('a' to 'z').iterator
while (ite.hasNext) print(ite.next)
abcdefghijklmnopqrstuvwxyz
Out[104]:
null
Seq
The Seq collections are ordered.
In [105]:
val s = ('a' to 'z')
Out[105]:
[[a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z]]
In [106]:
s(scala.util.Random.nextInt(25))
Out[106]:
u
In [107]:
s.segmentLength(v => (v < 'd'), 1) // Length of the longest sequence according to a predicate and an index
Out[107]:
2
In [108]:
s.prefixLength(v => (v < 'd')) // Length of the longest sequence according to a predicate from index 0
Out[108]:
3
In [109]:
s.indexWhere(v => "hello world" contains v.toString) // first indice according to a predicate
Out[109]:
3
In [110]:
s.lastIndexWhere(v => "hello world" contains v.toString) // last indice according to a predicate
Out[110]:
22
In [111]:
s.indexOf('z')
Out[111]:
25
In [112]:
s.startsWith(Seq('a', 'b'))
Out[112]:
true
In [113]:
s.endsWith(Seq('y', 'z'))
Out[113]:
true
In [114]:
s.reverse
Out[114]:
[[z, y, x, w, v, u, t, s, r, q, p, o, n, m, l, k, j, i, h, g, f, e, d, c, b, a]]
In [115]:
s.indexOfSlice(Seq('k', 'l'))
Out[115]:
10
Usual operations
Add / remove an element
Operation Description Collection
c += e ; c += (e1,..,en) ; c -= e ; c -= (e1,..,en) Adds and removes elements by modifying the collection. Mutable
c - e ; c - (e1,..,en) Retire des éléments, retourne une nouvelle collection. ArrayBuffer, Map, Set
Update
Operation Description Collection
c(k) = e ; c.update(k,
Updates the item in the indexed collection k. mutable.Map, mutable.Seq
e)
Assemblist operations
Opération Description Collection
c ++ c2 Union. Iterable
Union, Intersection et
c | c2 ; c & c2 ; c &~ c2 Set
Difference
Cast
In [116]:
Seq(1).toArray
Seq(1).toBuffer
Seq(1).toList
Seq((1, 2)).toMap
Seq(1).toStream
Seq(1).toString
Seq(1).toVector
Seq(1).toTraversable
Seq(1).toIndexedSeq
Seq(1).toIterable
Set(1).toSeq
Seq(1).toSet
Out[116]:
Set(1)
In [117]:
val collec = 1 to 100 by 8
collec.to[List] // in general
Out[117]:
[[1, 9, 17, 25, 33, 41, 49, 57, 65, 73, 81, 89, 97]]
Operations of Iterables
In [118]:
val collec = (1 to 100 by 6) ++ Seq(4)
Out[118]:
[[1, 7, 13, 19, 25, 31, 37, 43, 49, 55, 61, 67, 73, 79, 85, 91, 97, 4]]
In [119]:
collec.size
Out[119]:
18
In [120]:
collec.count(_%2 == 0)
Out[120]:
1
In [121]:
collec.isEmpty
Out[121]:
false
In [122]:
collec.nonEmpty
Out[122]:
true
In [123]:
collec.sameElements(collec)
Out[123]:
true
In [124]:
collec.forall(_ > 0)
Out[124]:
true
In [125]:
collec.exists(_ < 0)
Out[125]:
false
In [126]:
collec.sum
Out[126]:
837
In [127]:
collec.product
Out[127]:
-320583804
In [128]:
collec.min
Out[128]:
1
In [129]:
collec.max
Out[129]:
97
Transformation of a collection
map, flatMap
In [149]:
def range(n: Int) = 0 to n
Out[149]:
range: (n: Int)scala.collection.immutable.Range.Inclusive
In [150]:
Seq(1, 2, 3, 4).map(range)
Out[150]:
[[Range(0, 1), Range(0, 1, 2), Range(0, 1, 2, 3), Range(0, 1, 2, 3, 4)]]
In [151]:
Seq(1, 2, 3, 4).flatMap(range) // merges collections
Out[151]:
[[0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4]]
reduceLeft, reduceRight
In [152]:
(1 to 5).reduceLeft(_-_)
Out[152]:
-13
In [153]:
(1 to 5).reduceRight(_-_)
Out[153]:
3
foldRight, foldLeft
scanLeft, scanRight
Same as fold by saving the intermediate results in a collection
In [156]:
Seq(1, 2, 4, 1, 3, 4).scanLeft(Set[Int]()) ((s, e) => s + e)
Out[156]:
[[Set(), Set(1), Set(1, 2), Set(1, 2, 4), Set(1, 2, 4), Set(1, 2, 4, 3), Set(1, 2, 4, 3)]]
Operations of Seq
In [161]:
val s = Seq(5, 6, 2, 8, -3, 0, 2, 8)
Out[161]:
[[5, 6, 2, 8, -3, 0, 2, 8]]
In [162]:
s.indexOf(2)
Out[162]:
2
In [163]:
s.lastIndexOf(2)
Out[163]:
6
In [164]:
s.indexOfSlice(Seq(2, 8))
Out[164]:
2
In [165]:
s.lastIndexOfSlice(Seq(2, 8))
Out[165]:
6
In [166]:
s.indexWhere(_ % 2 == 0)
Out[166]:
1
In [167]:
s.prefixLength(_ > 0)
Out[167]:
4
In [168]:
s.segmentLength(_ > 0, 3)
Out[168]:
1
In [169]:
s.contains(1)
Out[169]:
false
In [170]:
s.containsSlice(Seq(8, -3))
Out[170]:
true
In [171]:
s.startsWith(Seq(0))
Out[171]:
false
In [172]:
s.endsWith(Seq(2, 8))
Out[172]:
true
In [173]:
s.reverse
Out[173]:
[[8, 2, 0, -3, 8, 2, 6, 5]]
In [174]:
s.intersect(Seq(1, 7, 8, 3))
Out[174]:
[[8]]
In [175]:
s.diff(Seq(1, 7, 8, 3, 2))
Out[175]:
[[5, 6, -3, 0, 2, 8]]
In [176]:
s.slice(0, 4).permutations.toList
Out[176]:
[[List(5, 6, 2, 8), List(5, 6, 8, 2), List(5, 2, 6, 8), List(5, 2, 8, 6), List(5, 8, 6, 2), List(5, 8, 2, 6), List(6, 5,
2, 8), List(6, 5, 8, 2), List(6, 2, 5, 8), List(6, 2, 8, 5), List(6, 8, 5, 2), List(6, 8, 2, 5), List(2, 5, 6, 8),
List(2, 5, 8, 6), List(2, 6, 5, 8), List(2, 6, 8, 5), List(2, 8, 5, 6), List(2, 8, 6, 5), List(8, 5, 6, 2), List(8, 5,
2, 6), List(8, 6, 5, 2), List(8, 6, 2, 5), List(8, 2, 5, 6), List(8, 2, 6, 5)]]
In [177]:
s.slice(0, 4).combinations(2).toList
Out[177]:
[[List(5, 6), List(5, 2), List(5, 8), List(6, 2), List(6, 8), List(2, 8)]]
In [178]:
s.sorted
Out[178]:
[[-3, 0, 2, 2, 5, 6, 8, 8]]
In [179]:
s.sortWith(_ > _)
Out[179]:
[[8, 8, 6, 5, 2, 2, 0, -3]]
In [180]:
s.sortBy(_ - scala.util.Random.nextInt(8)) // transforms the collection before sorting
Out[180]:
[[2, -3, 2, 0, 8, 6, 5, 8]]
Parallel collections
par
To understand where a parallel collection can be useful, it helps to think about how they work. Conceptually, you can imagine a collection being split
into different chunks; your algorithm is then applied to the chunks, and at the end of the operation, the different chunks are recombined. To be used
only if the order is not important.
In [181]:
1 to 100 par
Out[181]:
ParRange(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,
31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
91, 92, 93, 94, 95, 96, 97, 98, 99, 100)
In [182]:
(1 to 100 par) seq
Out[182]:
[[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, 31, 32,
33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62,
63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92,
93, 94, 95, 96, 97, 98, 99, 100]]
In [183]:
val start = System.nanoTime
val r = 0 to 1000000
val r = 0 to 1000000
Lazy view
In [185]:
val v = (1 to 5).view.map(_ * 3)
println(v) // nothing is evaluated
SeqViewM(...)
Out[185]:
null
In [186]:
println(v(3))
12
In [187]:
println(v.force)
Vector(3, 6, 9, 12, 15)
private
In [190]:
class ClassA {private val foo ="bar"} // access only for this
(new ClassA).foo
<console>:16: error: value foo in class ClassA cannot be accessed in ClassA
(new ClassA).foo
^
protected
In [191]:
class ClassA {protected def hey(s: String) = println(s"Hey $s !")} // access for this and children
Out[191]:
defined class ClassA
In [192]:
class ClassB extends ClassA {hey("John")}
new ClassB
Hey John !
Out[192]:
$line221.$read$$iw$$iw$ClassB@7afb1ba1
In [193]:
class ClassC {
hey("John")
(new ClassA).hey("John")
}
<console>:17: error: method hey in class ClassA cannot be accessed in ClassA
Access to protected method hey not permitted because
enclosing class ClassC is not a subclass of
class ClassA where target is defined
(new ClassA).hey("John")
^
Constructors
In [198]:
class ClassA(attribute1: Int, attribute2: Int){ //main constructors
def this(){ // another constructor
this(0, 0)
}
override def toString: String = s"$attribute1, $attribute2"
}
Out[198]:
defined class ClassA
In [199]:
new ClassA(30, 40).toString
Out[199]:
30, 40
In [200]:
new ClassA().toString
Out[200]:
0, 0
In [201]:
class ClassA(val attribute1: Int, private var attribute2: Int){ // attributes are accessible with getters and setters
def this(){ // another constructor
this(0, 0)
}
override def toString: String = s"$attribute1, $attribute2"
}
Out[201]:
defined class ClassA
In [202]:
class Fraction(val numerator: Int, val denominator: Int){
require(denominator != 0, "denominator can't be equals 0")
}
Out[202]:
defined class Fraction
In [203]:
new Fraction(1, 2)
Out[203]:
$line231.$read$$iw$$iw$Fraction@8344059
In [204]:
new Fraction(1, 0)
java.lang.IllegalArgumentException: requirement failed: denominator can't be equals 0
at scala.Predef$.require(Predef.scala:224)
... 48 elided
Nested Classes
In [205]:
class ClassA {
classAalias => // defined an alias for ClassA.this
val id = System.nanoTime
class ClassB {
def hey = println("Hello")
def id = ClassA.this.id // access the context of the surrounding class
def id2 = classAalias.id
}
def buildClassB = new ClassB
def bar(v: ClassA#ClassB) = println("Hello") // accepts all varieties of ClassA and ClassB
}
Out[205]:
defined class ClassA
In [206]:
val a = new ClassA
val b = a.buildClassB
b.hey
a.bar(b)
println(a.id, b.id, b.id2)
Hello
Hello
(228877232490212,228877232490212,228877232490212)
Out[206]:
null
Anonymous Classes
In [207]:
val person = new {
val name: String = "John"
def presentation(): Unit = println(s"My name is $name.")
}
person.presentation()
My name is John.
Out[207]:
null
In [208]:
def printName(person: {val name: String}) = { // {val name: String} is a type.
println(person.name)
}
printName(person)
John
Out[208]:
null
Transtyping
In [211]:
val obj1: Seq[Int] = Seq.empty
obj1.isInstanceOf[Iterable[Int]]
Out[211]:
true
In [212]:
obj1.asInstanceOf[Iterable[Int]]
Out[212]:
[[]]
In [213]:
obj1.getClass
Out[213]:
class scala.collection.immutable.Nil$
Object
Singleton Object
In [214]:
object Object { // Usually contains the static elements
val foo = 0
def bar = foo
}
Out[214]:
$line245.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$
$iw$Object$@7ad16797
In [215]:
Object.bar
Out[215]:
0
Any Methods
In [216]:
class ClassA(var a: Int = 0) {
override def toString =s"$a"
case class
In [222]:
case class File(name: String, suffix: String)
Out[222]:
defined class File
In [223]:
val file: File = File("test", "scala")
Out[223]:
File(test,scala)
In [224]:
file.name
Out[224]:
test
In [225]:
val File(name, suffix) = file
Out[225]:
scala
In [226]:
file.copy(name="test1")
Out[226]:
File(test1,scala)
Inheritance
extends
In [227]:
class Insect
Out[227]:
defined class Insect
In [228]:
class Ant extends Insect
Out[228]:
defined class Ant
override
In [231]:
class Class1 {
def method = println("class1")
}
Out[231]:
defined class Class1
In [232]:
class Class2 extends Class1 {
override def method = println("class2")
}
Out[232]:
defined class Class2
In [233]:
val c = new Class2
c.method
class2
Out[233]:
null
Abstract class
In [234]:
abstract class Class1 {
var indefinedVar: Int
def undefinedMethod()
}
Out[234]:
defined class Class1
final
In [235]:
final class Class1
Out[235]:
defined class Class1
In [236]:
class Class2 extends Class1
<console>:13: error: illegal inheritance from final class Class1
class Class2 extends Class1
^
In [237]:
class Class1 {
final def method = {}
}
Out[237]:
defined class Class1
In [238]:
class Class2 extends Class1 {
override def method = {}
}
<console>:14: error: overriding method method in class Class1 of type => Unit;
method method cannot override final member
override def method = {}
^
Implicit conversions
Implicit class
In [239]:
implicit class StringFile(s: String) {
def splitFile: Option[(String, String)] = {
s.split('.') match {
case Array(name, suffix) => Some((name, suffix))
case _ => None
}
}
}
Out[239]:
defined class StringFile
In [240]:
"hey".splitFile
Out[240]:
None
In [241]:
"hey.scala".splitFile
Out[241]:
Some((hey,scala))
Trait
In [245]:
trait Trait1 {
def foo = {}
}
trait Trait2 {
def bar = {}
}
Evaluation Rules
Call by value: evaluates the function arguments before calling the function
Call by name: evaluates the function first, and then evaluates the arguments if need be
// sum takes a function that takes an integer and returns an integer then
// returns a function that takes two integers and returns an integer
def sum(f: Int => Int): (Int, Int) => Int = {
def sumf(a: Int, b: Int): Int = f(a) + f(b)
sumf
}
// same as above. Its type is (Int => Int) => (Int, Int) => Int
def sum(f: Int => Int)(a: Int, b: Int): Int = { ... }
Currying
Converting a function with multiple arguments into a function with a single argument that returns another function.
def f(a: Int, b: Int): Int // uncurried version (type is (Int, Int) => Int)
def f(a: Int)(b: Int): Int // curried version (type is Int => Int => Int)
To curry an existing function :
val f2: (Int, Int) => Int = f // uncurried version (type is (Int, Int) => Int)
val f3: Int => Int => Int = f2.curried // transform it to a curried version (type is Int => Int => Int)
val f4: (Int, Int) => Int = Function.uncurried(f3) // go back to the uncurried version (type is (Int, Int) => Int)
Classes
class MyClass(x: Int, val y: Int,
var z: Int) { // Defines a new type MyClass with a constructor
// x will not be available outside MyClass
// val will generate a getter for y
// var will generate a getter and a setter for z
require(y > 0, "y must be positive") // precondition, triggering an IllegalArgumentException if not met
def this (x: Int) = { ... } // auxiliary constructor
def nb1 = x // public method computed every time it is called
private def test(a: Int): Int = { ... } // private method
val nb3 = x + y // computed only once
override def toString = // overridden method
x + ", " + y
}
Operators
myObject myMethod 1 is the same as calling myObject.myMethod(1)
Operator (i.e. function) names can be alphanumeric, symbolic (e.g. x1, *, +?%&, vector_++, counter_=)
The precedence of an operator is determined by its first character, with the following increasing order of priority:
(all letters)
|
^
&
< >
= !
:
+ -
* / %
(all other special characters)
The associativity of an operator is determined by its last character: Right-associative if ending with :, Left-associative otherwise.
Note that assignment operators have lowest precedence. (Read Scala Language Specification 2.9 sections 6.12.3, 6.12.4 for more info)
Class hierarchies
abstract class TopLevel { // abstract class
def method1(x: Int): Int // abstract method
def method2(x: Int): Int = { ... }
}
object MyObject extends TopLevel { ... } // defines a singleton object. No other instance can be created
To create a runnable application in Scala:
object Hello {
def main(args: Array[String]) = println("Hello world")
}
or
Type Parameters
Conceptually similar to C++ templates or Java generics. These can apply to classes, traits or functions.
def myFct[T <: TopLevel](arg: T): T = { ... } // T must derive from TopLevel or be TopLevel
def myFct[T >: Level1](arg: T): T = { ... } // T must be a supertype of Level1
def myFct[T >: Level1 <: TopLevel](arg: T): T = { ... }
Variance
Given A <: B
If C[A] <: C[B], C is covariant
If C[A] >: C[B], C is contravariant
Otherwise C is nonvariant
class Array[+T] {
def update(x: T)
} // variance checks fails
Find out more about variance in lecture 4.4 and lecture 4.5
Pattern Matching
Pattern matching is used for decomposing data structures:
unknownObject match {
case MyClass(n) => ...
case MyClass2(a, b) => ...
}
Here are a few example patterns
Options
Pattern matching can also be used for Option values. Some functions (like Map.get) return a value of type Option[T] which is either a
value of type Some[T] or the value None:
val myMap = Map("a" -> 42, "b" -> 43)
def getMapValue(s: String): String = {
myMap get s match {
case Some(nb) => "Value found: " + nb
case None => "No value found"
}
}
getMapValue("a") // "Value found: 42"
getMapValue("c") // "No value found"
Most of the times when you write a pattern match on an option value, the same expression can be written more concisely using
combinator methods of the Option class. For example, the function getMapValue can be written as follows:
def getMapValue(s: String): String =
myMap.get(s).map("Value found: " + _).getOrElse("No value found")
Collections
Scala defines several collection classes:
Base Classes
Iterable (collections you can iterate on)
Seq (ordered sequences)
Set
Map (lookup data structure)
Immutable Collections
List (linked list, provides fast sequential access)
Stream (same as List, except that the tail is evaluated only on demand)
Vector (array-like type, implemented as tree of blocks, provides fast random access)
Range (ordered sequence of integers with equal spacing)
String (Java type, implicitly converted to a character sequence, so you can treat every string like a Seq[Char])
Map (collection that maps keys to values)
Set (collection without duplicate elements)
Mutable Collections
Most of the immutable collections above have a mutable counterpart, e.g.:
Array (Scala arrays are native JVM arrays at runtime, therefore they are very performant)
Scala also has mutable maps and sets; these should only be used if there are performance issues with immutable types
Examples
NOTE: For the correct code convention of using postfix or not, read this
val s = (1 to 6).toSet
s map (_ + 2) // adds 2 to each element of the set
// Operations on sequences
val xs = List(...)
xs.length // number of elements, complexity O(n)
xs.last // last element (exception if xs is empty), complexity O(n)
xs.init // all elements of xs but the last (exception if xs is empty), complexity O(n)
xs take n // first n elements of xs
xs drop n // the rest of the collection after taking n elements
xs splitAt n // same as (xs take n, xs drop n)
xs(n) // the nth element of xs, complexity O(n)
xs ++ ys // concatenation, complexity O(n)
xs.reverse // reverse the order, complexity O(n)
xs updated(n, x) // same list than xs, except at index n where it contains x, complexity O(n)
xs indexOf x // the index of the first element equal to x (-1 otherwise)
xs contains x // same as xs indexOf x >= 0
xs filter p // returns a list of the elements that satisfy the predicate p
xs filterNot p // filter with negated p
xs partition p // same as (xs filter p, xs filterNot p)
xs takeWhile p // the longest prefix consisting of elements that satisfy p
xs dropWhile p // the remainder of the list after any leading element satisfying p have been removed
xs span p // same as (xs takeWhile p, xs dropWhile p)
xs exists p // true if there is at least one element for which predicate p is true
xs forall p // true if p(x) is true for all elements
xs zip ys // returns a list of pairs which groups elements with same index together
xs.unzip // opposite of zip: returns a pair of two lists
xs flatMap f // applies the function to all elements and concatenates the result
xs.sum // sum of elements of the numeric collection
xs.product // product of elements of the numeric collection
xs.max // maximum of collection
xs.min // minimum of collection
xs.flatten // flattens a collection of collection into a single-level collection
xs groupBy f // returns a map which points to a list of elements
xs.distinct // sequence of distinct entries (removes duplicates)
// Operations on maps
val myMap = Map("I" -> 1, "V" -> 5, "X" -> 10) // create a map
myMap("I") // => 1
myMap("A") // => java.util.NoSuchElementException
myMap get "A" // => None
myMap get "I" // => Some(1)
myMap.updated("V", 15) // returns a new map where "V" maps to 15 (entry is updated)
// if the key ("V" here) does not exist, a new entry is added
// Operations on Streams
val xs = Stream(1, 2, 3)
val xs = Stream.cons(1, Stream.cons(2, Stream.cons(3, Stream.empty))) // same as above
(1 to 1000).toStream // => Stream(1, ?)
x #:: xs // Same as Stream.cons(x, xs)
// In the Stream's cons operator, the second parameter (the tail)
// is defined as a "call by name" parameter.
// Note that x::xs always produces a List
def integers(start: Int = 0): Stream[Int] = start #:: integers(start + 1) // infinite sequence of integers starting at "start"
integers(0) drop 10 take 100 // New stream starting at 10
Ordering
There is already a class in the standard library that represents orderings: scala.math.Ordering[T] which contains comparison functions
such as lt() and gt() for standard types. Types with a single natural ordering should inherit from the trait scala.math.Ordered[T].
import math.Ordering
For-Comprehensions
A for-comprehension is syntactic sugar for map, flatMap and filter operations on collections.
The general form is for (s) yield e
Example 1
// list all combinations of numbers x and y where x is drawn from
// 1 to M and y is drawn from 1 to N
for (x <- 1 to M; y <- 1 to N)
yield (x,y)
is equivalent to
Translation Rules
A for-expression looks like a traditional for loop but works differently internally
This means you can use a for-comprehension for your own type, as long as you define map, flatMap and filter.
For more, see lecture 6.5.
Example 2
for {
i <- 1 until n
j <- 1 until i
if isPrime(i + j)
} yield (i, j)
is equivalent to
(1 until n).flatMap(i => (1 until i).filter(j => isPrime(i + j)).map(j => (i, j)))