Tehnografi.com - Технологические новости, обзоры и советы

FlatMap и CompactMap, объясненные на Swift

Следующая статья поможет вам: FlatMap и CompactMap, объясненные на Swift

Вернуться в блог

В Swift есть множество функций, полезных для преобразования коллекций и последовательностей. В этом руководстве по разработке приложений мы обсудим карты(_:), FlatMap(_:) и CompactMap(_:).

Вот на чем мы сосредоточимся:

  1. Как map(_:) преобразует коллекцию или последовательность, применяя к ней замыкание
  2. Как FlatMap(_:) может сгладить входной массив после вызова map(_:)
  3. Как CompactMap(_:) удаляет ноль из входного массива

В предыдущем уроке мы обсуждали, как можно использовать фильтр(_:) и сокращение(_:). Эти функции высшего порядка очень полезны для преобразования коллекций в сжатой и содержательной форме.

В качестве быстрого напоминания о функциях высшего порядка в Swift нашей отправной точкой является функция map(_:). Эта функция применяет преобразование к каждому элементу последовательности, например массива или словаря.

Вот пример:

пусть числа = [2, 3, 4, 5]
пусть результат = Numbers.map({ $0 * $0 })

печать (результат)

Давайте разберем это:

Сначала мы создаем массив чисел с несколькими целочисленными значениями. Затем функция map(_:) вызывается для чисел, и ее результат присваивается результату.

Функция map(_:) имеет один параметр — замыкание, которое возвращает результат $0 * $0. $0 соответствует первому параметру замыкания, т.е. числу преобразуемых чисел.

Мы вычисляем квадрат каждого числа в числах. По сути, операция $0 * $0 вызывается для каждого числа в числах, и результирующий массив присваивается результату. Вы преобразуете – или «сопоставляете» – один массив в другой.

Преобразование массива с помощью карты(_:) похоже на использование цикла for, но гораздо более кратко. Так:

пусть числа = [2, 3, 4, 5]
вар результат = [Int]()

для числа в цифрах {
результат += [number * number]
}

печать (результат)

Вот еще один способ взглянуть на это. С помощью map(_:) входной массив чисел преобразуется в другой массив чисел. Так:

2 => 2 * 2 => 4
3 => 3 * 3 => 9
4 => 4 * 4 => 16
5 => 5 * 5 => 25

Такие функции, как map(_:), называются функциями высшего порядка, поскольку они принимают на вход функцию, а не обычные значения. Функции высшего порядка также могут выводить функции, что полезно для парадигмы программирования, называемой функциональным программированием.

Технически вы можете вызывать функции более высокого порядка, такие как map(_:), в любой последовательности. Сюда входят такие коллекции, как массивы, словари и множества, диапазоны, такие как 1…100, и так называемые итераторы. По сути, все, что выглядит как «список» значений.

В конце этого руководства мы обсудим, почему функции высшего порядка полезны. Давайте сначала перейдем к изучению FlatMap(_:) и CompactMap(_:).

Функция FlatMap(_:) аналогична функции map(_:) за исключением того, что она «сглаживает» результирующий массив. Вот пример:

пусть числа = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
пусть результат = Numbers.FlatMap({ $0 })

печать (результат)

Приведенный выше код начинается с вложенного массива целых чисел. Массив чисел состоит из массива из трех массивов, каждый из которых содержит по три числа.

Замыкание { $0 } просто возвращает первый аргумент замыкания, то есть отдельные вложенные массивы. Никакой трансформации или операции не происходит. Однако когда вы вызываете FlatMap(_:) для массива чисел, вместо Map(_:) вы получаете сглаженный массив отдельных чисел. В отличие от чисел входного массива, результирующий массив не содержит вложенных массивов!

Давайте посмотрим на другой пример. Представьте, что вы работаете с четырьмя группами жирафов и хотите создать одну группу жирафов ростом выше определенного роста. Вот как это сделать:

пусть жирафы = [[5, 6, 9], [11, 2, 13, 20], [1, 13, 7, 8, 2]]
пусть самый высокий = giraffes.flatMap({ $0.filter({ $0 > 10 }) })

распечатать (самый высокий)

Видите, как жирафы содержат массив массивов? В приведенном выше коде функция filter(_:) вызывается для каждого вложенного массива внутри жирафов. Нам нужны только целые числа (жирафы!), превышающие 10. Полученные массивы сводятся в один «плоский» массив и присваиваются самым высоким.

Представьте, что произошло бы, если бы мы использовали Map(_:) вместо FlatMap(_:). Полученный массив не будет сглажен. Вместо этого это было бы следующее:

[[], [11, 13, 20], [13]]

Важно отметить, что функция FlatMap(_:) сначала вызывает карту(_:) для элементов массива, а затем выравнивает их. Вот почему что-то вроде следующего не работает:

пусть числа = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
пусть результат = Numbers.FlatMap({ $0 * 2 })

В приведенном выше коде $0 относится к массивам внутри чисел. Умножить массив на два невозможно, поэтому этот код не работает.

Разумно думать о плоском отображении как о просмотре массива в одном измерении меньше. Вы начинаете с двумерного массива и заканчиваете одномерным массивом после FlatMap(_:).

А как насчет использования FlatMap(_:) с опциями? Давайте посмотрим на это дальше.

Название «компактная карта» основано на идее, что удаление нулевых элементов из массива делает массив более компактным. Аналогично, название «плоская карта» происходит от сглаживания массива. А «отображение» — это математическая концепция, согласно которой вы связываете значения одного набора с другим набором.

Функция CompactMap(_:) удаляет нулевые значения из входного массива. Это очень полезно при работе с опциями.

До Swift 4.1 функцию FlatMap(_:) (см. выше) также можно было использовать для фильтрации нулевых значений из сглаженных массивов. Начиная с Swift 4.1+, для этой цели теперь используется явный CompactMap(_:).

Вот пример:

пусть числа = [“5”, “42”, “nine”, “100”, “Bob”]
пусть результат = Numbers.compactMap({ Int($0) })

печать (результат)

Посмотрите, что происходит? Самая важная часть кода — Int($0). Это берет отдельную строку из чисел с $0 и пытается преобразовать ее в целое число с помощью инициализатора Int().

Этот инициализатор Int() не работает: он может возвращать ноль (необязательно), поэтому его тип возвращаемого значения — Int?. В результате тип возвращаемого значения преобразования отображения: [Int?] – массив необязательных целых чисел.

[Optional(5), Optional(42), nil, Optional(100), nil]

Функция CompactMap(_:) автоматически удаляет нулевые элементы из возвращаемого массива после вызова для него карты(_:). Таким образом, тип возвращаемого значения не является обязательным.

[5, 42, 100]

В приведенном выше коде тип результата: [Int]. Если бы вы использовали карту(_:), тип возвращаемого значения был бы [Int?]. И вам потребовался бы дополнительный шаг, чтобы развернуть значения из массива и работать с ними.

Прежде чем мы обсудим реальные варианты использования плоских и компактных карт, давайте кратко рассмотрим эти функции высшего порядка и их назначение.

  1. Функция map(_:) применяет замыкание к входной коллекции и возвращает преобразованную коллекцию.
  2. Функция FlatMap(_:) делает то же самое, а также сглаживает полученную коллекцию.
  3. Функция CompactMap(_:) делает то же самое, что и map(_:), а также удаляет ноль из результирующей коллекции.

Работая с map(_:), FlatMap(_:) и CompactMap(_:) в абстрактном виде, иногда трудно представить варианты их практического использования. Давайте обсудим, почему вы хотите их использовать.

Использование таких функций, как map(_:), для применения преобразований к последовательностям имеет несколько преимуществ:

  • Это более лаконично, чем использование цикла for, поскольку вам не нужны временные переменные и многострочный блок for in { }.
  • Обычно вы можете написать вызов map(_:) в одной строке, что (обычно) делает ваш код более читабельным.
  • Такие функции, как map(_:), можно объединять в цепочки, поэтому вы можете применять несколько преобразований к последовательности одно за другим.

В целом функции высшего порядка полезны, поскольку позволяют применять функцию к последовательности значений. Вместо процедурного кодирования преобразования вы можете просто применить функцию и получить результат обратно.

Наиболее практичным вариантом использования FlatMap(_:) является работа с входными значениями, которые сгруппированы или вложены, но выходное значение, которое вам нужно, должно быть одномерным.

Например, в музыкальном приложении вы можете иметь три массива: песни, исполнители и плейлисты. Вы объединяете их в один массив, вызываете для него FlatMap(_:), выбираете песни, исполнителей и плейлисты, для которых isFavorite имеет значение true, и в конечном итоге получаете один плоский список элементов, которые были добавлены в избранное.

Практический вариант использования CompactMap(_:) — работа с преобразованием, которое может возвращать ноль. Вы сэкономите несколько тривиальных шагов, позволив CompactMap(_:) немедленно отфильтровать нулевые значения. Дополнительным преимуществом является необязательный тип возвращаемого значения CompactMap(_:); Для сравнения, функция filter(_:) вернула бы необязательное значение, если бы вы отфильтровали ноль.

Вы можете комбинировать FlatMap(_:) и CompactMap(_:) и даже фильтровать(_:) или уменьшать(_:_:). Представьте, что вы создаете приложение для социальных сетей. Вы хотите создать шкалу публикаций для пользователя. Вы используете 3 запроса для выбора идентификаторов сообщений для пользователя, например, из сообщений подписчиков, рекламных объявлений и популярных тем.

  • Вы можете использовать карту(_:) для расширения этих идентификаторов в реальные объекты Post.
  • Вы можете использовать FlatMap(_:), чтобы объединить три группы в одну коллекцию.
  • Вы можете использовать CompactMap(_:) для удаления сообщений, которые невозможно расширить.

Дальнейшее чтение

Стоит узнать о картах (_:), FlatMap(_:) и CompactMap(_:), поскольку они делают ваш код более кратким и читабельным. Вы можете добавить больше функционального программирования в код вашего приложения. Как только вы к ним привыкнете, вы не сможете поверить, что можете выполнять свою работу без них.

Особенно стоит отметить различия между Map(_:), FlatMap(_:) и CompactMap(_:). Первый применяет преобразование к последовательности, второй выравнивает результирующий массив, а третий удаляет нулевые значения перед возвратом результата. Потрясающий!

Table of Contents