May 13, 2021 Scala
Scala provides some nice collections.
Refer to Effective Scala's views on how to use the collection.
scala> val numbers = List(1, 2, 3, 4)
numbers: List[Int] = List(1, 2, 3, 4)
The set is not repeated
scala> Set(1, 1, 2)
res0: scala.collection.immutable.Set[Int] = Set(1, 2)
A u-group is a simple collection of logic that combines elements without using classes.
scala> val hostPort = ("localhost", 80)
hostPort: (String, Int) = (localhost, 80)
Unlike sample classes, the metagroup cannot get a field by name, but instead reads the object using the position undersrage, which is based on 1, not 0.
scala> hostPort._1
res0: String = localhost
scala> hostPort._2
res1: Int = 80
The group can be well combined with pattern matching.
hostPort match {
case ("localhost", port) => ...
case (host, port) => ...
}
When you create a two-element group, you can use a special syntax:
->
scala> 1 -> 2
res0: (Int, Int) = (1,2)
Refer to Effective Scala's view of http://twitter.github.com/effectivescala/#Functional (http://twitter.github.com/effectivescala/#Functional programming-Destructuring bindings) ("dismantling" a yuangroup).
It can hold basic data types.
Map(1 -> 2)
Map("foo" -> "bar")
This may seem like a special syntax, but don't forget that
->
The Map() method also uses a list of variable parameters learned from the first lesson:
Map(1 -> "one", 2 -> "two")
to
Map((1, "one"), (2, "two"))
the first argument is the key of the map and the second parameter is the value of the map.
The value of the map can be a map or even a function.
Map(1 -> Map("foo" -> "bar"))
Map("timesTwo" -> { timesTwo(_) })
Option is a container that represents a possible inclusion of values.
Option's basic interface is this:
trait Option[T] {
def isDefined: Boolean
def get: T
def getOrElse(t: T): T
}
Option itself is generic and has two sub-classes: Some.T. or None
Let's look at an example of using Option:
Map.get uses Option as its return value, indicating that the method may not return the value you requested.
scala> val numbers = Map("one" -> 1, "two" -> 2)
numbers: scala.collection.immutable.Map[java.lang.String,Int] = Map(one -> 1, two -> 2)
scala> numbers.get("two")
res0: Option[Int] = Some(2)
scala> numbers.get("three")
res1: Option[Int] = None
Now that our data seems to be stuck in Option, how do we get it?
Intuitively, it is possible to use conditional judgment on the isDefined method.
// We want to multiply the number by two, otherwise return 0.
val result = if (res1.isDefined) {
res1.get * 2
} else {
0
}
We recommend using getOrElse or pattern matching to handle this result.
getOrElse makes it easy for you to define a default value.
val result = res1.getOrElse(0) * 2
Pattern matching works naturally with Option.
val result = res1 match {
case Some(n) => n * 2
case None => 0
}
Refer to Effective Scala's comments on http://twitter.github.com/effectivescala/#Functional options (programming-options).
List(1, 2, 3) map squared
applies the squared function to each element in the list and returns a
List(1, 4, 9)
W
e call this operation map combination. (
If you want a better definition, you might like the description of the combiner
on
Stackoverflow.)
They are often used in standard data structures.
Map applies a function to each element in the list, returning a list of elements that were applied.
scala> numbers.map((i: Int) => i * 2)
res0: List[Int] = List(2, 4, 6, 8)
or pass in a section of the application function
scala> def timesTwo(i: Int): Int = i * 2
timesTwo: (i: Int)Int
scala> numbers.map(timesTwo _)
res0: List[Int] = List(2, 4, 6, 8)
Foreach is much like map, but does not return a value.
Foreach is only used for functions
[side-effects]
. . side-effects.
scala> numbers.foreach((i: Int) => i * 2)
Nothing returned.
You can try to store the return value, but it will be a Unit type (i.e. void)
scala> val doubled = numbers.foreach((i: Int) => i * 2)
doubled: Unit = ()
Filter removes any elements that calculate the result of false for the incoming function.
A function that returns a Boolean value is often referred to as a
[或判定函数]
.
scala> numbers.filter((i: Int) => i % 2 == 0)
res0: List[Int] = List(2, 4)
scala> def isEven(i: Int): Boolean = i % 2 == 0
isEven: (i: Int)Boolean
scala> numbers.filter(isEven _)
res2: List[Int] = List(2, 4)
Zip aggregates the contents of two lists into a pair list.
scala> List(1, 2, 3).zip(List("a", "b", "c"))
res0: List[(Int, String)] = List((1,a), (2,b), (3,c))
partition splits the list using the given predicate function.
scala> val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> numbers.partition(_ % 2 == 0)
res0: (List[Int], List[Int]) = (List(2, 4, 6, 8, 10),List(1, 3, 5, 7, 9))
Find returns the first element in the collection that matches the predicate function.
scala> numbers.find((i: Int) => i > 5)
res0: Option[Int] = Some(6)
Drop deletes the first i element
scala> numbers.drop(5)
res0: List[Int] = List(6, 7, 8, 9, 10)
DropWhile deletes the element until the first element that matches the predicate function is found. For example, if we use the dropWhile odd function on the numbers list, 1 will be discarded (but 3 will not be discarded because it is "protected" by 2).
scala> numbers.dropWhile(_ % 2 != 0)
res0: List[Int] = List(2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> numbers.foldLeft(0)((m: Int, n: Int) => m + n)
res0: Int = 55
0 is the initial value (remember that the numbers are of the List(Int) type), and m is an adder.
Observe the operation directly:
scala> numbers.foldLeft(0) { (m: Int, n: Int) => println("m: " + m + " n: " + n); m + n }
m: 0 n: 1
m: 1 n: 2
m: 3 n: 3
m: 6 n: 4
m: 10 n: 5
m: 15 n: 6
m: 21 n: 7
m: 28 n: 8
m: 36 n: 9
m: 45 n: 10
res0: Int = 55
Like foldLeft, it just runs the opposite way.
scala> numbers.foldRight(0) { (m: Int, n: Int) => println("m: " + m + " n: " + n); m + n }
m: 10 n: 0
m: 9 n: 10
m: 8 n: 19
m: 7 n: 27
m: 6 n: 34
m: 5 n: 40
m: 4 n: 45
m: 3 n: 49
m: 2 n: 52
m: 1 n: 54
res0: Int = 55
Flatten flatten flattens the nested structure into a hierarchical collection.
scala> List(List(1, 2), List(3, 4)).flatten
res0: List[Int] = List(1, 2, 3, 4)
FlatMap is a common combination that combines mapping with mapping and flattening. FlatMap requires a function that handles nested lists and then strings the results together.
scala> val nestedNumbers = List(List(1, 2), List(3, 4))
nestedNumbers: List[List[Int]] = List(List(1, 2), List(3, 4))
scala> nestedNumbers.flatMap(x => x.map(_ * 2))
res0: List[Int] = List(2, 4, 6, 8)
Think of it as a quick action to "map first and flatten later":
scala> nestedNumbers.map((x: List[Int]) => x.map(_ * 2)).flatten
res1: List[Int] = List(2, 4, 6, 8)
This example calls map first, and then flatten immediately, which is the characteristic of "combinors" and the nature of these functions.
Refer to Effective Scala's
comments on http://twitter.github.com/effectivescala/#Functional
flatMap
Now we've learned some functions on the collection.
We'll try to write our own function combinations.
Interestingly, each function combination shown above can be implemented using the fold method. Let's look at some examples.
def ourMap(numbers: List[Int], fn: Int => Int): List[Int] = {
numbers.foldRight(List[Int]()) { (x: Int, xs: List[Int]) =>
fn(x) :: xs
}
}
scala> ourMap(numbers, timesTwo(_))
res0: List[Int] = List(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)
Why
List[Int]()?Scala
not smart enough to understand that your goal is to accumulate results in an empty list of Int types.
All of the function combinations shown can be used on map. Map can be thought of as a list of binary groups, so you write a function that deals with a binary group of keys and values.
scala> val extensions = Map("steve" -> 100, "bob" -> 101, "joe" -> 201)
extensions: scala.collection.immutable.Map[String,Int] = Map((steve,100), (bob,101), (joe,201))
Now filter out entries with a telephone extension number below 200.
scala> extensions.filter((namePhone: (String, Int)) => namePhone._2 < 200)
res0: scala.collection.immutable.Map[String,Int] = Map((steve,100), (bob,101))
Because parameters are metagroups, you must use the location acquirer to read their keys and values.
Fortunately, we can actually use pattern matching to extract keys and values more gracefully.
scala> extensions.filter({case (name, extension) => extension < 200})
res0: scala.collection.immutable.Map[String,Int] = Map((steve,100), (bob,101))