С нуля до распределенных приложений.
xs.tail - все элементы xs кроме первого
scala> List(1, 2, 3).tail
res0: List[Int] = List(2, 3)
scala> List(1).tail
res1: List[Int] = List()
scala> List().tail
java.lang.UnsupportedOperationException: tail of empty list
xs.init - все элементы xs кроме последнего
xs take n - коллекция состоящая из первых n элементов xs (или любых n элементов, если порядок элементов в коллекции не определен)
scala> List(1, 2, 3).take(5)
res0: List[Int] = List(1, 2, 3)
scala> Set(1, 2, 3, 4, 5).take(2)
res1: scala.collection.immutable.Set[Int] = Set(5, 1)
scala> List(1, 2, 3).take(2)
res2: List[Int] = List(1, 2)
xs drop n - часть коллекции за вычетом (xs take n)
xs takeWhile p - префикс коллекции наибольшей длины в котором все элементы удовлетворяют функции-предикату p
scala> List(1, 2, 3, 4, 5).takeWhile(_ < 3)
res0: List[Int] = List(1, 2)
scala> Set(1, 2, 3, 4, 5).takeWhile(_ < 3)
res1: scala.collection.immutable.Set[Int] = Set()
xs dropWhile p - коллекция без наибольшего префикса элементов удовлетворяющих p
xs filter p - коллекция состоящая из всех элементов xs удовлетворяющих фильтру p
scala> List(1, 2, 3, 4, 5).filter(_ % 2 == 0)
res11: List[Int] = List(2, 4)
xs withFilter p - “ленивый фильтр”
xs filterNot p - коллекция состоящая из всех элементов xs не удовлетворяющих p
xs splitAt n - делит xs на индексе n, возвращая пару коллекций (xs take n, xs drop n)
scala> List("a", "b", "c", "d").splitAt(2)
res0: (List[String], List[String]) = (List(a, b),List(c, d))
xs span p - разбивает xs с начала относительно предиката p, возвращая пару коллекций (xs takeWhile p, xs.dropWhile p)
scala> val (smaller, bigger) = List(1, 2, 3, 4, 5).span(_ < 3)
smaller: List[Int] = List(1, 2)
bigger: List[Int] = List(3, 4, 5)
xs partition p - делит коллекцию на пару из двух коллекций, первая из элементов удовлетворяющих предикату p, а вторая нет (xs filter p, xs.filterNot p)
scala> scala> val (evens, odds) = List(1, 2, 3, 4, 5).partition(_ % 2 == 0)
evens: List[Int] = List(2, 4)
odds: List[Int] = List(1, 3, 5)
xs groupBy f - группирует элементы xs в соответствии с ключом-дискриминатором заданным функцией f
scala> List(-2, -1, 0, 1, 2).groupBy(x => x * x)
res0: scala.collection.immutable.Map[Int,List[Int]] = Map(4 -> List(-2, 2), 1 -> List(-1, 1), 0 -> List(0))
scala> Set("a", "b", "aa", "bb", "ccc").groupBy(_.length)
res1: scala.collection.immutable.Map[Int,scala.collection.immutable.Set[String]] = Map(2 -> Set(aa, bb), 1 -> Set(a, b), 3 -> Set(ccc))
xs forall p - возвращает true если все элементы xs удовлетворяют p
scala> List(1, 2, 3).forall(_ < 5)
res0: Boolean = true
xs exists p - возвращает true, если хоть один элемент в xs удовлетворяет p
scala> List(1, 2, 3).exists(_ > 5)
res0: Boolean = false
xs count p - возвращает количество элементов в xs удовлетворяющих предикату p
scala> List(1, 2, 3, 4, 5).count(_ % 2 == 0)
res0: Int = 2
Эти функции позволяют заменить большую часть императивных циклов (все остальные можно заменить рекурсией). Суть свёртки заключается в том, чтобы взяв некоторое начальное значение применить некоторую бинарную операцию к нему и к первому элементу элементу коллекции, затем применить эту же операцию к результату и второму элементу коллекции и т.д.
Рассмотрим простой императивный цикл суммирующий все элементы xs:
scala> val xs = List(1, 2, 3, 4, 5)
var sum = 0
for (x <- xs)
sum = sum + x
sum
res0: Int = 15
Здесь можно выделить начальное значение sum равное 0 и бинарную операцию (a, b) => a + b. Мы применяем эту операцию к 0 и первому элементу коллекции, затем к результату и второму элементу и т.д.
То есть, суть свёртки в том, чтобы взяв в качестве аккумулятора начальное значение z, прогнать его через всю коллекцию применяя некоторую операцию op.
xs.foldLeft(z)(op) - применить бинарную операцию op к последовательным элементам xs двигаясь слева направо и начиная с z (левая свёртка)
// (((((0 + 1) + 2) + 3) + 4) + 5)
scala> List(1, 2, 3, 4, 5).foldLeft(0) { (a, b) => a + b }
res0: Int = 15
// начальное значение не должно иметь один тип с элементами xs
scala> List("a", "bb", "ccc").foldLeft(0) { (acc, x) => acc + x.length }
res0: Int = 6
xs.foldRight(z)(op) - применить бинарную операцию к последовательным элементам xs двигаясь справа налево и начиная с z (правая свёртка)
// (1 - (2 - (3 - (4 - (5 - 0)))))
scala> List(1, 2, 3, 4, 5).foldRight(0){ (x, acc) => x - acc }
res0: Int = 3
scala> List(1, 2, 3, 4, 5).foldRight(0){ (x, acc) => acc - x }
res0: Int = 15
(z /: xs)(op) - то же, что и xs.foldLeft(z)(op)
(xs :\ z)(op) - то же, что и xs.foldRight(z)(op)
xs reduceLeft op - foldLeft для непустой коллекции, где в качестве начального значения аккумулятора берется ее первый элемент
scala> List(1, 2, 3).reduceLeft {_ + _}
res0: Int = 6
scala> List(1, 2, 3).filter(_ > 5).reduceLeft {_ + _}
java.lang.UnsupportedOperationException: empty.reduceLeft
xs reduceRight op - для foldRight то же, что и reduceLeft для foldLeft
Некоторые виды свёрток применяются настолько часто, что для них есть специализированные методы.
xs.sum - возвращает сумму элементов xs
scala> List(1, 2, 3, 4, 5).sum
res0: Int = 15
// то же самое через foldLeft
scala> List(1, 2, 3, 4, 5).foldLeft(0) {_ + _}
res1: Int = 15
xs.product - возвращает произведение элементов xs
xs.min - минимальный из элементов коллекции xs, если на них определен порядок
scala> List(1, 2, 3).min
res0: Int = 1
scala> Map(1 -> "a", 2 -> "b").min
res1: (Int, String) = (1,a)
scala> List(List(1, 2), List(1, 2, 3)).min
<console>:8: error: No implicit Ordering defined for List[Int].
List(List(1, 2), List(1, 2, 3)).min
// то же самое явной свёрткой
scala> List(4, 3, 1, 2).reduceLeft { (acc, x) => if (x < acc) x else acc }
res3: Int =
// или еще проще
scala> List(4, 3, 1, 2).reduceLeft(Math.min)
res4: Int = 1
xs.max - максимальный из упорядоченных элементов коллекции xs
xs.mkString(sep) - перевести в строку все элементы коллекции разделив их опциональным разделителем sep
scala> List(42, 43, 44).mkString(" -> ")
res0: String = 42 -> 43 -> 44
// то же самое через явную свёртку
scala> List(42, 43, 44).map(_.toString).reduceLeft { (str, x) => str + " -> " + x}
res1: String = 42 -> 43 -> 44