Whirlwind Overview + Advanced Features
Online (These are highly recommended!)
Martin Odersky's First Steps in Scala
Note: Interpreter output differs from current version of Scala.
Twitter's Scala School
Matt Might's Scala In Small Bites
API Search Engine scalex.org
Books
Programming in Scala, by Odersky, Spoon & Venners
Scala for the Impatient, by Horstmann
Like ML, Scala is statically typed
The easiest way to get started is with Scala's REPL
$ scala
Welcome to Scala version 2.10.3 ...
Type in expressions to have them evaluated.
Type :help for more information.
scala>
Enter expressions and have them evaluated.
scala> 2
res0: Int = 2
scala> 2 + 3
res1: Int = 5
So when you type:
2 + 3
the compiler sees the method call:
2.+(3)
So +
is just a method call! (As are many other things...)
Furthermore, unlike ML (and like Java) Scala supports "overloading".
scala> 2 + "cat"
res3: String = 2cat
scala> 2 + 3.0
res4: Double = 5.0
Each of these calls the appropriate method on the receiver Int
.
But don't get carried away...
scala> 2 + true
<console>:8: error: overloaded method value + with alternatives:
(x: Double)Double <and>
(x: Float)Float <and>
(x: Long)Long <and>
(x: Int)Int <and>
(x: Char)Int <and>
(x: Short)Int <and>
(x: Byte)Int <and>
(x: String)String
cannot be applied to (Boolean)
2 + true
^
You have your basic built-in types...
scala> 4.2 * 6.7
res8: Double = 28.14
scala> true || false
res9: Boolean = true
scala> 'c'
res10: Char = c
String
s are borrowed from Java...
scala> "cat"
res25: java.lang.String = cat
scala> "cat" + "dog"
res26: java.lang.String = catdog
scala> "cat" * 3
res26: java.lang.String = catcatcat
... and you can take whatever you like from Java.
scala> import java.util.Date
import java.util.Date
scala> new Date()
res2: java.util.Date = Thu Nov 08 17:40:50 PST 2012
So every Java library is usable in Scala!
Notice that the REPL gives names to resulting values.
scala> "Cool!"
res2: String = Cool!
scala> res2
res3: String = Cool!
Two ways to introduce variables:
val
keyword:scala> val x = 2
x: Int = 2
val x = e
is like OCaml's let x = e
So don't try to change (re-assign) such a variable!
scala> x = x + 1
<console>:8: error: reassignment to val
x = x + 1
^
scala> var z = 1
z: Int = 1
scala> z += 1
scala> z
res1: Int = 2
scala> z = 3
z: Int = 3
We will revisit val
and var
when we talk about objects and fields.
Scala comes equipped with modern amenities like tuples.
scala> val t = ("cat", 12)
t: (java.lang.String, Int) = (cat,12)
scala> val t2 = ("mickey", 12, "mouse")
t2: (java.lang.String, Int, java.lang.String)
= (mickey,12,mouse)
scala> val t3 = ("mickey", (12, "mouse"))
t3: (java.lang.String, (Int, java.lang.String))
= (mickey,(12,mouse))
Tuple types are written (T1, T2)
and (T1, T2, T3)
and so on
(unlike (T1 * T2)
as in OCaml).
To access the tuple elements, you can use pattern matching
scala> t
res7: (java.lang.String, Int) = (cat,12)
scala> val (p, q) = t
p: java.lang.String = cat
q: Int = 12
or the tuple field accessors ._1
or ._2
, etc.
scala> t._1
res5: java.lang.String = cat
scala> t._2
res6: Int = 12
You can sequence expressions to get bigger expressions:
scala> :paste
// Entering paste mode (ctrl-D to finish)
{ println("hello world")
2 + 8 }
// Exiting paste mode, now interpreting.
hello world
res0: Int = 10
Paste-mode lets you enter multi-line expressions in REPL.
{...}
is called a block.Many ways to define functions. One way:
def inc(x: Int): Int = {
x + 1
}
Notice explicit argument and return types...
Call a function in the usual way (parentheses required):
scala> inc(4)
res3: Int = 5
No explicit return
, just a result expression (like ML):
def adder(x: Int, y: Int): Int = {
println("adder args: (x=%d, y=%d)".format(x, y))
val z = x + y
println("adder ret: (z=%d)".format(z))
z
}
Block evaluates to last expression
scala> adder(2, 3)
adder args: (x=2, y=3)
adder ret: (z=5)
res6: Int = 5
def curriedAdder(x: Int)(y: Int): Int = {
println("curriedAdder args: %d %d".format(x, y))
x + y
}
Curried functions require parens around each argument!
scala> curriedAdder(2)(3)
curriedAdder args: 2 3
res7: Int = 5
What about partial application as in OCaml?
scala> curriedAdder(2)(3)
curriedAdder args: 2 3
res7: Int = 5
scala> val add2 = curriedAdder(2)
error: missing arguments for method curriedAdder ...
To partially apply, need _
for all other arguments:
scala> val plus2 = curriedAdder(2)(_)
plus2: Int => Int = <function1>
scala> plus2(3)
curriedAdder args: 2 3
res8: Int = 5
You can partially apply any of the arguments...
scala> val plus3 = curriedAdder(_:Int)(3)
plus3: Int => Int = <function1>
scala> plus3(2)
curriedAdder args: 2 3
res9: Int = 5
Whoa!
Note: sometimes Scala will complain and ask for a type annotation.
A.k.a anonymous functions, like OCaml's fun x -> e
scala> (x: Int) => x + 1
res3: (Int) => Int = <function1>
scala> (x: Int, y: Int) => x + y
res2: (Int, Int) => Int = <function2>
Notice the types of the functions
Can call directly:
scala> ((x: Int) => x + 1)(8)
res3: Int = 9
Usually makes sense to bind to a name first:
scala> val inc = (x: Int) => x + 1
inc: (Int) => Int = <function1>
scala> inc(5)
res5: Int = 6
Functions are just objects with apply
method.
scala> object inc { def apply(x: Int) = x + 1 }
Call them like so:
scala> inc(5)
res: Int = 6
Functions are just objects with apply
method.
When you write f(args)
Scala reads f.apply(args)
Anything with apply
can be called.
scala> val str = "megalomaniac"
str: String = megalomaniac
scala> str(2)
res: Char = g
The usual suspects ...
if-else
try-catch
while
-loops
for
-loops
def fac(n: Int): Int = {
if (n > 0) { n * fac (n-1) } else { 1 }
}
The {...}
around then
and else
expressions can sometimes be omitted.
def fac(n: Int): Int = {
if (n > 0) n * fac (n-1) else 1
}
But, it's okay to always write {...}
.
Scala has pattern matching, too:
def fac(n: Int): Int =
n match {
case n if (n > 0) => n * fac(n-1)
case _ => 1
}
More on this later...
Unlike ML, you have to write down some types in Scala.
Must write argument types for all functions.
Must write return types for recursive functions.
(Of course, you can also write them for non-recursive functions.)
scala> def fac(n: Int) =
| n match {
| case n if (n > 0) => n * fac(n-1)
| case _ => 1
| }
error: recursive method fac needs result type ...
Scala's Local Type Inference figures out the rest!
scala> def f() = 1
f: ()Int
scala> f()
res1: Int = 1
Can be called without parentheses:
scala> f
res2: Int = 1
Put the body expression inside {...}
.
import java.io.File
def fileContents(file: String): List[String] = {
val f = new java.io.File(file)
try {
scala.io.Source.fromFile(f).getLines().toList
} catch {
case e: java.io.FileNotFoundException => {
println("WTF! No such file exists")
List()
}
}
}
Create a file called file.txt
with some lines of text.
Try running:
scala> val lines = fileContents("file.txt")
lines: List[String] = List(Hello, world)
Try running it with a junk file:
scala> fileContents("foobar")
WTF! No such file exists
res10: List[String] = List()
while
-loopsOld-school while
loops:
def fac(n: Int) = {
var res = 1
var count = n
while (count > 0) {
res *= count
count -= 1
}
res
}
for
-loopsAnd, of course, for
-loops, too:
scala> for (i <- 1 to 5) println(i)
1
2
3
4
5
for
-loopsOr, if you want to step by something other than 1
...
scala> for (i <- 1 to 5 by 2) println(i)
1
3
5
for
-loopsOr, count in the other direction ...
scala> for (i <- 5 to 1 by -1) println(i)
5
4
3
2
1
for
-loopsActually, for
-loops are quite special.
scala> for (i <- "special") println(i)
s
p
e
c
i
a
l
Hmm, what's going on?
Scala bundled with with a rich set of Collections
Arrays
Lists
(Hash) Sets
(Hash) Maps
Streams ...
Some are immutable. Some are mutable.
(Mutable) Arrays
scala> val a = Array("cow", "dog", "mouse")
res14: Array[java.lang.String] = Array(cow, dog, mouse)
Note the type: Array[String]
.
Arrays have a fixed length
scala> a.length
res15: Int = 3
(Mutable) Arrays can be randomly accessed.
scala> a(2)
res16: java.lang.String = mouse
a(2)
read as lookup method call a.apply(2)
(Mutable) Arrays can be randomly updated.
scala> a(0) = "aardvark"
scala> a
res19: Array[java.lang.String] =
Array(aardvark, dog, mouse)
a(0) = "aardvark"
read as update method call a.update(0, "aardvark")
for
-loops(Mutable) Arrays can also be looped over...
scala> for (animal <- a) println(animal)
aardvark
dog
mouse
(Immutable) Lists
scala> val xs = List(1,2,3,4)
xs: List[Int] = List(1, 2, 3, 4)
scala> val ys = List("cat", "dog", "moose", "goose")
ys: List[java.lang.String] =
List(cat, dog, moose, goose)
Cannot change a list!
scala> ys(0) = "kite"
error: value update is not a member of
List[java.lang.String] ...
Quite a bit like ML lists.
scala> val zs = "chicken" :: ys
zs: List[java.lang.String] =
List(chicken, cat, dog, moose, goose)
Can be accessed via pattern-matching:
def listConcat(xs: List[String]): String =
xs match {
case Nil => ""
case h::t => h + listConcat(t)
}
Which you can call like so:
scala> listConcat(zs)
res10: chickencatdogmoosegoose
You can also append lists.
scala> List(1,2,3) ++ List(4,5,6)
res11: List[Int] = List(1,2,3,4,5,6)
Alternate syntax:
scala> List(1,2,3) ::: List(4,5,6)
res11: List[Int] = List(1,2,3,4,5,6)
And you can loop over lists, too:
scala> for (animal <- zs) println(animal)
chicken
cat
dog
moose
goose
Key-Value Maps (immutable by default)
scala> val numNames =
Map("one" -> 1, "two" -> 2, "three" -> 3)
numNames:
scala.collection.immutable.Map[String,Int]
= Map(one -> 1, two -> 2, three -> 3)
Key-Value Maps (immutable by default)
You can lookup the value of a key much like arrays:
scala> numNames("three")
res12: Int = 3
Key-Value Maps (immutable by default)
If the value doesn't exist though...
scala> numNames("nine")
java.util.NoSuchElementException: key not found: nine
...
Exception!
Key-Value Maps (immutable by default)
Moral, look before you leap:
scala> numNames.contains("nine")
res13: Boolean = false
Or, using the cleaner notation:
scala> numNames contains "nine"
res14: Boolean = false
Key-Value Maps (immutable by default)
Would be nice to extend a map with new key-value bindings...
How do you think it's done?
Key-Value Maps (immutable by default)
Would be nice to extend a map with new key-value bindings...
scala> numNames + ("nine" -> 9)
res15:
scala.collection.immutable.Map[String,Int]
= Map(one -> 1, two -> 2, three -> 3, nine -> 9)
Note: the above is a brand new map ...
scala> numNames contains "nine"
res16: Boolean = false
Key-Value Maps (immutable by default)
Would be nice to extend a map with new key-value bindings ...
scala> val newMap = numNames + ("nine" -> 9)
newMap:
scala.collection.immutable.Map[String,Int]
= Map(one -> 1, two -> 2, three -> 3, nine -> 9)
... So, bind the result to a new variable.
scala> newMap("nine")
res17: Int = 9
(Pssst.) There are mutable Key-Value Maps, too...
scala> import scala.collection.mutable.HashMap
scala> val mmap : HashMap[String, Int] = HashMap()
scala> mmap += "mon" -> 1
scala> mmap += "tue" -> 2
scala> mmap += "wed" -> 3
scala> mmap("tue")
res18: Int = 2
Note: type parameters for the key (String
) and value (Int
)
val
var
(scala.collection.immutable.)Map
scala.collection.mutable.HashMap
res
?import scala.collection.mutable.HashMap
val mutMap: HashMap[String, Int] = HashMap()
mutMap += "mon" -> 1
mutMap += "mon" -> 2
val res = mutMap("mon") // mutMap.apply("mon")
A. No value, Type Error (cannot update val)
B. No value, Runtime Exception (key not found)
C. 1: Int
D. 2: Int
E. None: Option[Int]
res
?import scala.collection.immutable.Map
var immutMap : Map[String, Int] = Map()
var other = immutMap
immutMap = immutMap + ("mon" -> 1)
immutMap = immutMap + ("mon" -> 2)
val res = other("mon")
A. No value, Type Error
B. No value, Runtime Exception (NotFound)
C. 1: Int
D. 2: Int
E. None: Option[Int]
res
?var mutMap : HashMap[String, Int] = HashMap()
var other = mutMap
mutMap += "mon" -> 1
mutMap += "mon" -> 2
val res = other("mon")
A. No value, Type Error
B. No value, Runtime Exception (NotFound)
C. 1: Int
D. 2: Int
E. None: Option[Int]
Nasty run-time exceptions...
scala> var mutMap = HashMap[String, Int]("a" -> 1)
scala> mutMap("b")
... Exception ...
Defensive programming:
scala> if (mutMap contains "b") mutMap("b") else -1
res0: Int = -1
Instead, the get
method returns an option type!
scala> mutMap.get("a")
res1: Option[Int] = Some(1)
scala> mutMap.get("b")
res2: Option[Int] = None
Pattern match result:
scala> mutMap.get("b") match {
| case Some(n) => n
| case None => -1
| }
res3: Int = -1
res
?var mutMap : HashMap[String, Int] = HashMap()
var other = mutMap + ("mon" -> 1)
val res = (other.get("mon"), mutMap.get("mon"))
A. No value, Type Error
B. (None, None) : (Option[Int], Option[Int])
C. (Some(1), None) : (Option[Int], Option[Int])
D. (None, Some(1)) : (Option[Int], Option[Int])
E. (Some(1), Some(1)) : (Option[Int], Option[Int])
res
?var mutMap : HashMap[String, Int] = HashMap()
var other = mutMap + ("mon" -> 1)
val res = (other.get("mon"), mutMap.get("mon"))
Drumroll please...
mutMap
and other
aliases for the same map?HashMap.+
method produces a new map!mutMap + ("mon" -> 1)
(referred to by other
) is a new map.What if we tried this instead, without the other
variable?
var mutMap : HashMap[String, Int] = HashMap()
mutMap = mutMap + ("mon" -> 1)
val res = (other.get("mon"), mutMap.get("mon"))
HashMap.+
produces a new Map
, but not necessarily a HashMap
.mutMap
.other
inferred to be Map[String, Int]
.)+
+=
All collection objects equipped with HOFs!
filter
All collection objects equipped with HOFs!
scala> "MaSsIvEaTtAcK".filter((c: Char) => c.isUpper)
res19: String = MSIETAK
Can omit argument type in this case:
scala> "MaSsIvEaTtAcK".filter(c => c.isUpper)
res19: String = MSIETAK
Or, with equivalent, simpler syntax for HOFs:
scala> "MaSsIvEaTtAcK".filter(_.isUpper)
res20: String = MSIETAK
filter
All collection objects equipped with HOFs!
scala> List(1,2,3,4,5,6,7,8).filter(_ % 2 == 0)
res21: List[Int] = List(2,4,6,8)
scala> Array(1,2,3,4,5,6,7,8).filter(_ % 2 == 0)
res21: Array[Int] = Array(2,4,6,8)
filter
With Map
, the filter
is over key-value tuples:
scala> numNames.filter(kv => kv._1.length == 3)
res24: scala.collection.immutable.Map[String,Int]
= Map(one -> 1, two -> 2)
Or, using the shorthand:
scala> numNames.filter(_._1.length == 3)
res24: scala.collection.immutable.Map[String,Int]
= Map(one -> 1, two -> 2)
filter
With Map
, the filter
is over key-value tuples:
Or, using an anonymous function with pattern matching:
scala> numNames.filter({case (k,v) => k.length == 3})
res23: scala.collection.immutable.Map[String,Int]
= Map(one -> 1, two -> 2)
map
All collection objects equipped with HOFs!
Strings...
scala> "MaSsIvEaTtAcK".map(_.toUpper)
res24: String = MASSIVEATTACK
Lists...
scala> List(1,2,3).map(_ * 100)
res25: List[Int] = List(100, 200, 300)
Arrays...
scala> Array("cat", "dog").map(_.map(_.toUpper))
res26: Array[String] = Array(CAT, DOG)
... are all collections.
foreach
foreach
is like map
but does an action, returns dummy value ()
.
scala> Array("cat", "dog").foreach(println(_))
cat
dog
So, what's a for
-loop...?
val a = Array("cat", "dog")
for (i <- a) { println(i) }
for
-loops Revisitedfor
-loop is just a HOF!
Writing
for (a <- thing) { body }
is just the same as
thing.foreach(a => body)
for
-loops RevisitedSo, you can loop over all collections:
scala> numNames.foreach(
{case (k, v) => println(k + " ~~~> " + v)})
one ~~~> 1
two ~~~> 2
three ~~~> 3
Or, if you prefer:
scala> for ((k, v) <- numNames)
println(k + " ~~~> " + v)
one ~~~> 1
two ~~~> 2
three ~~~> 3
for
-loops RevisitedAnother example:
scala> val dir = new java.io.File(".")
dir: java.io.File = .
scala> for (f <- dir.listFiles) println(f)
./slides.markdown
./scratch.markdown
./Monoid.scala
./Freq.scala
...
for
-loops RevisitedSometimes, want to loop over some elements and skip others.
For example, to print names of all .scala
files:
scala> for (f <- dir.listFiles
if f.getName.endsWith(".markdown"))
{ println(f) }
./Monoid.scala
./Freq.scala
for
-loops RevisitedSometimes, want to loop over some elements and skip others.
for (x <- coll if cond) { body }
This is really nice syntactic sugar for which of the following?
A. coll.filter(x => body)
B. coll.filter(x => cond).foreach(x => body)
C. coll.foreach(x => body)
D. coll.foreach(x => body).filter(x => body)
for
-loops RevisitedOften want to compute a collection as a value.
Enter yield
.
Suppose we have an Array
of String
s:
val words = Array("you", "are", "doing", "it", "wrong")
To turn it into a rant:
scala> val rant = for (w <- words) yield w.toUpperCase
rant: Array[java.lang.String] =
List(YOU, ARE, DOING, IT, WRONG)
Note: the output is also an Array
.
for
-loops RevisitedOften want to compute a collection as a value.
Enter yield
.
Works for any collection:
scala> for (w <- fileContents("file.txt"))
yield w.toUpperCase
res: List[String] = List(HELLO, WORLD)
for
-loops RevisitedOften want to compute a collection as a value.
for (x <- coll) yield expr
This is really nice syntactic sugar for which of the following?
A. coll.foreach(x => expr)
B. coll.filter(x => expr)
C. coll.map(x => expr)
for
-loops RevisitedSometimes want to compute a collection as a value ...
... after some processing.
For example, to find all .scala
files in a directory:
scala> val scalaFiles =
for (f <- dir.listFiles
if f.getName.endsWith(".scala"))
yield f
scalaFiles: Array[java.io.File] =
Array(./Monoid.scala, ./Freq.scala)
for
-loops RevisitedSometimes want to compute a collection as a value ...
... after some processing.
scala> import scala.io.Source._
scala> val fileSizes =
for (f <- dir.listFiles
if f.getName.endsWith(".scala"))
yield fromFile(f).length
fileSizes: Array[Int] = Array(349, 406)
for
-loops RevisitedSometimes want to compute a collection as a value ...
... after some processing.
for (x <- coll if cond) yield expr
Is really another way of writing
coll.filter(x => cond).map(x => expr)
for
-loopsYou can nest for
-loops, too:
scala> for (i <- 1 to 3; j <- 1 to 3) {
println ("i = %d and j = %d" format (i, j))
}
i = 1 and j = 1
i = 1 and j = 2
i = 1 and j = 3
i = 2 and j = 1
i = 2 and j = 2
i = 2 and j = 3
i = 3 and j = 1
i = 3 and j = 2
i = 3 and j = 3
for
-loopsYou can nest for
-loops, too.
And then return a collection as the result:
scala> for (i <- 1 to 3; j <- 1 to 3) yield (i, j)
res:
scala.collection.immutable.IndexedSeq[(Int, Int)]
= Vector((1,1), (1,2), (1,3),
(2,1), (2,2), (2,3),
(3,1), (3,2), (3,3))
for
-loopsNest for
-loops, and return a collection after filtering:
scala> val res = for (i <- List(1,2,3);
j <- List(4,5,6)
if i + j == 7)
yield (i, j)
What does this produce?
A. List()
B. List((1,6),(2,5),(3,4))
C. List(1,2,3,4,5,6)
D. List((1,4),(1,5),(1,6),(2,4),(2,5),(2,6),(3,4),(3,5),(3,6))
E. List((1, 4), (2, 5), (3, 6))
for
-loopsNest for
-loops, and return a collection after filtering.
Works for any collection.
scala> val res = for (i <- 1 to 3;
j <- 1 to 3
if i < j)
yield (i, j)
res: IndexedSeq[(Int, Int)] =
Vector((1,2), (1,3), (2,3))
So far, inner loop independent of outer but...
for
-loopsThe inner loop can also depend on outer loop:
scala> val ll = List(List(1,2), List(3,4))
scala> val res = for (xs <- ll;
x <- xs)
yield x
What is the value of res
?
A. List(1, 3)
B. List(List(1,2), List(3,4))
C. List((1,3), (1,4), (2,3), (2,4))
D. List(1,2,3,4)
E. List()
for
-loopsAnother example:
scala> for (w <- List("cat", "dog", "mouse"); c <- w)
yield c
res: List[Char] =
List(c, a, t, d, o, g, m, o, u, s, e)
Note the output type is also List
...
... like the top-level collection!
for
-loopsAnother example:
scala> for (w <- Array("cat", "dog", "mouse"); c <- w)
yield c
res: Array[Char] =
Array(c, a, t, d, o, g, m, o, u, s, e)
Note the output type is also Array
...
... like the top-level collection!
for
-loops RevisitedWait a minute! Remember this?
scala> for (i <- 1 to 5) println(i)
1
2
3
4
5
How does it work ?
for
-loops RevisitedWait a minute! Remember this?
1 to 5
is a method call 1.to(5)
scala> 1 to 5
res: scala.collection.immutable.Range.Inclusive =
Range(1, 2, 3, 4, 5)
for
-loops RevisitedIf you want a step:
scala> 1 to 10 by 2 // 1.to(10).by(2)
res3: scala.collection.immutable.Range =
Range(1, 3, 5, 7, 9)
scala> 10 to 1 by -1 // 10.to(1).by(-1)
res4: scala.collection.immutable.Range =
Range(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
scala> 10 to 1 by -2 // 10.to(1).by(-1)
res5: scala.collection.immutable.Range =
Range(10, 8, 6, 4, 2)
So, what does this do?
List(1,2,3,4,5).foldLeft(0)((x1, x2) => x1 + x2)
So, what does this do?
List(1,2,3,4,5).foldLeft(0)(_ + _)
Here, (_ + _)
is shorthand for
(x1, x2) => x1 + x2
And, what does this do?
def foo(n: Int, k: Int) =
(1 to k).map(_ => n).foldLeft(1)(_ * _)
How often does a Char
appear in a String
?
def freq(str: String): HashMap[Char, Int] = {
val freqMap = new HashMap[Char, Int]
for (c <- str) {
freqMap(c) = 1 + freqMap.getOrElse(c, 0)
// if(freqMap contains c){freqMap(c)}
// else { 0 }
}
freqMap
}
Let's generalize a bit (make it polymorphic).
def freq[A](xs: Iterable[A]): HashMap[A, Int] = {
val freqMap = new HashMap[A, Int]
for (x <- xs) {
freqMap(x) = 1 + freqMap.getOrElse(x, 0)
}
freqMap
}
Iterable[A]
describes objects that can be iterated over...
Can run it on String
s
scala> freq("caterpillar")
res:
scala.collection.mutable.HashMap[Char,Int]
= Map(c -> 1, a -> 2, e -> 1, i -> 1,
r -> 2, t -> 1, l -> 2, p -> 1)
or List
s
scala> freq(List(1,2,1,13,1,2,1,3,31,12,1))
res:
scala.collection.mutable.HashMap[Int,Int]
= Map(12 -> 1, 3 -> 1, 13 -> 1,
1 -> 5, 2 -> 2, 31 -> 1)
or ...
To make an executable, first put the functions in a file,
e.g. scala-crash.scala
:
object Freq { ... }
Compile with:
$ scalac scala-crash.scala
Run from command-line with:
$ scala Freq foo.txt
...
$ scala Freq bar.txt
...
Or (after compiling) run in the Scala REPL:
scala> val m = Freq("caterpillar")
scala> Freq.show(m, 1)
...
Another way to quickly load functions from a file:
scala> :load scala-crash.scala
scala> val m = Freq("caterpillar")
scala> Freq.show(m, 1)
e : #
t : #
p : #
a : ##
i : #
r : ##
l : ##
c : #
Handy way to discover APIs!
scala> "hello".<Hit Tab>
!= ## #asInstanceOf
+ ... ...
charAt ... ...
...
scala> val l = List(1, 2)
scala> l.<Hit Tab>
... filter ... foldLeft ... takeWhile ...