Вы не можете говорить о Scala, не вдаваясь в детали функций Map, flatMap, zip и Reduce. С помощью этих функций очень легко обрабатывать содержимое списков и работать с объектом Option. Обратите внимание, что на этом сайте вы можете найти еще несколько фрагментов:
- Отрывки Scala 1: складные
- Фрагменты Scala 2: список символов магии
- Отрывки Scala 3: списки вместе с картой, плоской картой, сжатием и уменьшением
Давайте начнем с опции карты. С опцией карты мы применяем функцию к каждому элементу списка и возвращаем это как новый список.
Мы можем использовать это для умножения каждого значения в списке:
1
2
3
4
5
|
scala> list1 res3: List[Int] = List( 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ) scala> list1.map(x=>x*x) res4: List[Int] = List( 0 , 1 , 4 , 9 , 16 , 25 , 36 , 49 , 64 , 81 , 100 ) |
Некоторые функции, которые вы применяете к списку, могут привести к появлению элементов Option. Возьмем, к примеру, следующую функцию:
1
2
3
4
5
|
scala> val evenify = (x:Int) => if (x % 2 == 0 ) Some(x) else None evenify: Int => Option[Int] = <function1> scala> list1.map(evenify) res6: List[Option[Int]] = List(Some( 0 ), None, Some( 2 ), None, Some( 4 ), None, Some( 6 ), None, Some( 8 ), None, Some( 10 )) |
Проблема в этом случае в том, что мы часто не заинтересованы в результатах None в нашем списке. Но как мы можем легко получить их? Для этого мы можем использовать flatMap. С flatMap мы можем обрабатывать списки последовательностей. Мы применяем предоставленную функцию к каждому элементу каждой последовательности в списке и возвращаем список, который содержит элементы каждой последовательности исходного списка. Пример гораздо проще понять:
01
02
03
04
05
06
07
08
09
10
11
|
scala> val list3 = 10 to 20 toList list3: List[Int] = List( 10 , 11 , 12 , 13 , 14 , 15 , 16 , 17 , 18 , 19 , 20 ) scala> val list2 = 1 to 10 toList list2: List[Int] = List( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ) scala> val list4 = List(list2, list3) list4: List[List[Int]] = List(List( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ), List( 10 , 11 , 12 , 13 , 14 , 15 , 16 , 17 , 18 , 19 , 20 )) scala> list4.flatMap(x=>x.map(y=>y* 2 )) res2: List[Int] = List( 2 , 4 , 6 , 8 , 10 , 12 , 14 , 16 , 18 , 20 , 20 , 22 , 24 , 26 , 28 , 30 , 32 , 34 , 36 , 38 , 40 ) |
Как видите, у нас есть два списка. В этом списке мы вызываем функцию flatMap. FlatMap обрабатывает каждую из двух записей по отдельности. В каждом из отдельных списков мы вызываем функцию map для дублирования каждой записи. Окончательный результат представляет собой один список, который содержит все записи, сведенные в один список.
Теперь давайте вернемся к функции evenify, которую мы видели ранее, и к списку элементов Option, которые у нас были.
01
02
03
04
05
06
07
08
09
10
11
|
scala> val list1 = 1 to 10 toList list1: List[Int] = List( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ) scala> list1.map(evenify) res3: List[Option[Int]] = List(None, Some( 2 ), None, Some( 4 ), None, Some( 6 ), None, Some( 8 ), None, Some( 10 )) scala> val list2 = list1.map(evenify) list2: List[Option[Int]] = List(None, Some( 2 ), None, Some( 4 ), None, Some( 6 ), None, Some( 8 ), None, Some( 10 )) scala> list2.flatMap(x => x) res6: List[Int] = List( 2 , 4 , 6 , 8 , 10 ) |
Легко, верно. И, конечно же, мы можем написать это в одну строку.
1
2
|
scala> list1.flatMap(x=>evenify(x)) res14: List[Int] = List( 2 , 4 , 6 , 8 , 10 ) |
Как видите, не так уж и сложно. Теперь давайте посмотрим на пару других функций, которые вы можете использовать в списках. Первый — почтовый индекс. И как следует из названия с этой функцией, мы можем объединить два списка вместе.
01
02
03
04
05
06
07
08
09
10
11
|
scala> val list = "Hello.World" .toCharArray list: Array[Char] = Array(H, e, l, l, o, ., W, o, r, l, d) scala> val list1 = 1 to 20 toList list1: List[Int] = List( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , 16 , 17 , 18 , 19 , 20 ) scala> list.zip(list1) res30: Array[(Char, Int)] = Array((H, 1 ), (e, 2 ), (l, 3 ), (l, 4 ), (o, 5 ), (., 6 ), (W, 7 ), (o, 8 ), (r, 9 ), (l, 10 ), (d, 11 )) scala> list1.zip(list) res31: List[(Int, Char)] = List(( 1 ,H), ( 2 ,e), ( 3 ,l), ( 4 ,l), ( 5 ,o), ( 6 ,.), ( 7 ,W), ( 8 ,o), ( 9 ,r), ( 10 ,l), ( 11 ,d)) |
Как только один список достигает конца, функция zip останавливается. У нас также есть функция zipAll, которая также обрабатывает оставшиеся элементы большего списка:
1
2
|
scala> list.zipAll(list1, 'a' , '1' ) res33: Array[(Char, AnyVal)] = Array((H, 1 ), (e, 2 ), (l, 3 ), (l, 4 ), (o, 5 ), (., 6 ), (W, 7 ), (o, 8 ), (r, 9 ), (l, 10 ), (d, 11 ), (a, 12 ), (a, 13 ), (a, 14 ), (a, 15 ), (a, 16 ), (a, 17 ), (a, 18 ), (a, 19 ), (a, 20 )) |
Если список с символами исчерпан, мы поместим букву «а», если список целых чисел исчерпан, мы поместим 1. У нас есть одна последняя функция zip для исследования, и это zipWithIndex. Еще раз, имя в значительной степени подводит итог того, что произойдет. Элемент индекса будет добавлен:
1
2
|
scala> list.zipWithIndex res36: Array[(Char, Int)] = Array((H, 0 ), (e, 1 ), (l, 2 ), (l, 3 ), (o, 4 ), (., 5 ), (W, 6 ), (o, 7 ), (r, 8 ), (l, 9 ), (d, 10 )) |
Итак, к последней из функций для изучения: уменьшить. С помощью Reduce мы обрабатываем все элементы в списке и возвращаем одно значение. С помощью ReduLeft и reduRight мы можем задать направление, в котором обрабатываются значения (при уменьшении это не гарантируется):
01
02
03
04
05
06
07
08
09
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
|
scala> list1 res51: List[Int] = List( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , 16 , 17 , 18 , 19 , 20 ) scala> val sum = (x:Int, y:Int) => {println(x,y) ; x + y} sum: (Int, Int) => Int = <function2> scala> list1.reduce(sum) ( 1 , 2 ) ( 3 , 3 ) ( 6 , 4 ) ( 10 , 5 ) ( 15 , 6 ) ( 21 , 7 ) ( 28 , 8 ) ( 36 , 9 ) ( 45 , 10 ) ( 55 , 11 ) ( 66 , 12 ) ( 78 , 13 ) ( 91 , 14 ) ( 105 , 15 ) ( 120 , 16 ) ( 136 , 17 ) ( 153 , 18 ) ( 171 , 19 ) ( 190 , 20 ) res52: Int = 210 scala> list1.reduceLeft(sum) ( 1 , 2 ) ( 3 , 3 ) ( 6 , 4 ) ( 10 , 5 ) ( 15 , 6 ) ( 21 , 7 ) ( 28 , 8 ) ( 36 , 9 ) ( 45 , 10 ) ( 55 , 11 ) ( 66 , 12 ) ( 78 , 13 ) ( 91 , 14 ) ( 105 , 15 ) ( 120 , 16 ) ( 136 , 17 ) ( 153 , 18 ) ( 171 , 19 ) ( 190 , 20 ) res53: Int = 210 scala> list1.reduceRight(sum) ( 19 , 20 ) ( 18 , 39 ) ( 17 , 57 ) ( 16 , 74 ) ( 15 , 90 ) ( 14 , 105 ) ( 13 , 119 ) ( 12 , 132 ) ( 11 , 144 ) ( 10 , 155 ) ( 9 , 165 ) ( 8 , 174 ) ( 7 , 182 ) ( 6 , 189 ) ( 5 , 195 ) ( 4 , 200 ) ( 3 , 204 ) ( 2 , 207 ) ( 1 , 209 ) res54: Int = 210 |
Помимо этих функций у нас также есть ReduOption (а также в вариантах reduLeftOption и reduRightOption). Эти функции будут возвращать Option вместо значения. Это может быть использовано для безопасной обработки пустых списков, что приведет к отсутствию.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
scala> list1.reduceRightOption(sum) ( 19 , 20 ) ( 18 , 39 ) ( 17 , 57 ) ( 16 , 74 ) ( 15 , 90 ) ( 14 , 105 ) ( 13 , 119 ) ( 12 , 132 ) ( 11 , 144 ) ( 10 , 155 ) ( 9 , 165 ) ( 8 , 174 ) ( 7 , 182 ) ( 6 , 189 ) ( 5 , 195 ) ( 4 , 200 ) ( 3 , 204 ) ( 2 , 207 ) ( 1 , 209 ) res65: Option[Int] = Some( 210 ) scala> val list3 = List() list3: List[Nothing] = List() scala> list3.reduceRightOption(sum) res67: Option[Int] = None |
Достаточно для этого фрагмента и для изучения API списков / коллекций на данный момент. В следующем фрагменте мы рассмотрим некоторые вещи из Scalaz, поскольку, хотя библиотека имеет дурную репутацию сложной, она предоставляет некоторые действительно приятные функции.
Ссылка: | Фрагменты Scala 3: Списки вместе с Map, flatmap, zip и сокращение от нашего партнера JCG Йоса Дирксена в блоге Smart Java . |