Skip to content

Latest commit

 

History

History
464 lines (356 loc) · 31.3 KB

lab7.md

File metadata and controls

464 lines (356 loc) · 31.3 KB

7. Функции обработки данных

Преобразование данных

Функция map

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

fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R>

где

  • T — тип элементов исходной коллекции.
  • R — тип элементов результирующей коллекции.
  • transform — функция, которая принимает элемент типа T и возвращает элемент типа R.

Примеры использования:

Преобразование списка целых чисел в список строк

val numbers = listOf(1, 2, 3, 4, 5)
val strings = numbers.map { it.toString() }
println(strings)  // Вывод: [1, 2, 3, 4, 5]

Удвоение каждого элемента списка

val numbers = listOf(1, 2, 3, 4, 5)
val doubled = numbers.map { it * 2 }
println(doubled)  // Вывод: [2, 4, 6, 8, 10]

Функция flatten

Функция flatten используется для объединения вложенных коллекций (например, списков списков) в одну плоскую коллекцию. Она применяется к коллекциям, элементами которых являются другие коллекции, и возвращает один плоский список, содержащий все элементы из вложенных коллекций.

fun <T> Iterable<Iterable<T>>.flatten(): List<T>

Примеры использования

Объединение списка списков в плоский список

val listOfLists = listOf(
    listOf(1, 2, 3),
    listOf(4, 5),
    listOf(6, 7, 8)
)

val flatList = listOfLists.flatten()
println(flatList)  // Вывод: [1, 2, 3, 4, 5, 6, 7, 8]

Функция flatMap

Функция высшего порядка flatMap объединяет в себе возможности map и flatten. Она применяется к коллекциям (спискам, множествам, массивам и т.д.) и позволяет преобразовать каждый элемент коллекции в другую коллекцию, а затем объединить все полученные коллекции в одну плоскую коллекцию.

fun <T, R> Iterable<T>.flatMap(transform: (T) -> Iterable<R>): List<R>

где

  • T — тип элементов исходной коллекции.
  • R — тип элементов результирующей коллекции.
  • transform — функция, которая преобразует каждый элемент типа T в коллекцию типа Iterable<R>.

Примеры использования

Преобразование списка списков в плоский список.

val listOfLists = listOf(
    listOf(1, 2, 3),
    listOf(4, 5),
    listOf(6, 7, 8)
)
val flatList = listOfLists.flatMap { it }
println(flatList)  // Вывод: [1, 2, 3, 4, 5, 6, 7, 8]

Преобразование списка строк в список символов

val strings = listOf("apple", "banana", "cherry")

val chars = strings.flatMap { it.toList() }
println(chars)  // Вывод: [a, p, p, l, e, b, a, n, a, n, a, c, h, e, r, r, y]

Функция associate

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

fun <T, K, V> Iterable<T>.associate(transform: (T) -> Pair<K, V>): Map<K, V>

где

  • T — тип элементов исходной коллекции.
  • K — тип ключей в результирующем Map.
  • V — тип значений в результирующем Map.
  • transform — функция, которая принимает элемент типа T и возвращает пару Pair<K, V>.

Примеры использования:

Преобразования множества целых чисел в Map с квадратами чисел в качестве значений

val numbers = setOf(1, 2, 3, 4, 5)
val numberToSquareMap = numbers.associate { it to it * it }
println(numberToSquareMap)  // Вывод: {1=1, 2=4, 3=9, 4=16, 5=25}

Функция groupBy

Функция groupBy используется для группировки элементов коллекции по заданному ключу. Результатом является Map, где ключом является значение, возвращаемое функцией-селектором, а значением — список элементов, которые соответствуют этому ключу. Эта функция особенно полезна, когда необходимо сгруппировать элементы по определенному признаку.

fun <T, K> Iterable<T>.groupBy(keySelector: (T) -> K): Map<K, List<T>>

где

  • T — тип элементов исходной коллекции.
  • K — тип ключей в результирующем Map.
  • keySelector — функция, которая принимает элемент исходной коллекции и возвращает ключ типа K. Эта функция определяет, по какому признаку будут группироваться элементы.

Примеры использования

Группировка строк по первой букве

val strings = listOf("Afghanistan", "Albania", "Algeria", "Bahamas", "Bahrain")

val groupedByFirstLetter = strings.groupBy { it.first() }
println(groupedByFirstLetter)
// Вывод: {A=[Afghanistan, Albania, Algeria], B=[Bahamas, Bahrain]}

Фильтрация данных

Функция filter

Функция высшего порядка filter позволяет отфильтровать элементы коллекции на основе заданного условия и вернуть новую коллекцию, содержащую только те элементы, которые удовлетворяют этому условию.

fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T>

где,

  • T — тип элементов исходной коллекции.
  • predicate — функция, которая принимает элемент исходной коллекции и возвращает Boolean, определяя должен ли он быть включен в результирующую коллекцию.

Примеры использования

Фильтрация четных чисел

val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

val evenNumbers = numbers.filter { it % 2 == 0 }
println(evenNumbers)  // Вывод: [2, 4, 6, 8, 10]

Фильтрация строк по длине

val strings = listOf("apple", "banana", "cherry", "date", "elderberry")

val longStrings = strings.filter { it.length > 5 }
println(longStrings)  // Вывод: ["banana", "cherry", "elderberry"]

Комбинирование данных

Функция zip используется для объединения двух коллекций в одну, создавая пары элементов из каждой коллекции. Результатом является новая коллекция, содержащая пары элементов, где каждая пара состоит из элемента из первой коллекции и соответствующего элемента из второй коллекции. Если коллекции имеют разную длину, результат будет содержать только пары, созданные до достижения конца более короткой коллекции.

fun <T, R> Iterable<T>.zip(other: Iterable<R>): List<Pair<T, R>>

где

  • T — тип элементов первой коллекции.
  • R — тип элементов второй коллекции.

Примеры использования

Объединение двух списков

val list1 = listOf(1, 2, 3)
val list2 = listOf("a", "b", "c")

val zippedList = list1.zip(list2)
println(zippedList)  // Вывод: [(1, "a"), (2, "b"), (3, "c")]

Объединение списков разной длины

val list1 = listOf(1, 2, 3, 4)
val list2 = listOf("a", "b")

val zippedList = list1.zip(list2)
println(zippedList)  // Вывод: [(1, "a"), (2, "b")]

Агрегирование данных

Функция reduce

Функция reduce используется для агрегирования элементов коллекции в одно значение, применяя заданную операцию к каждому элементу последовательно. Она начинает с первых двух элементов, применяет к ним операцию, затем применяет операцию к результату и следующему элементу, и так далее, пока не будут обработаны все элементы коллекции.

fun <S, T : S> Iterable<T>.reduce(operation: (acc: S, T) -> S): S

где

  • T — тип элементов исходной коллекции.
  • S — тип результирующего значения.
  • operation — функция, которая принимает два аргумента: аккумулятор (acc) и текущий элемент коллекции, и возвращает новое значение аккумулятора.

Примеры использования

Сумма элементов списка

val numbers = listOf(1, 2, 3, 4, 5)

val sum = numbers.reduce { acc, number -> acc + number }
println(sum)  // Вывод: 15

Объединение строк

val strings = listOf("a", "b", "c", "d")

val concatenated = strings.reduce { acc, string -> acc + string }
println(concatenated)  // Вывод: "abcd"

Функция fold

Функция fold используется для агрегирования элементов коллекции в одно значение, начиная с начального значения (аккумулятора) и применяя заданную операцию к каждому элементу последовательно. В отличие от reduce, fold позволяет задать начальное значение аккумулятора.

fun <T, R> Iterable<T>.fold(initial: R, operation: (acc: R, T) -> R): R

где

  • T — тип элементов исходной коллекции.
  • R — тип результирующего значения.
  • operation — функция, которая принимает два аргумента: аккумулятор (acc) и текущий элемент коллекции, и возвращает новое значение аккумулятора.

Примеры использования

Сумма элементов списка с начальным значением

val numbers = listOf(1, 2, 3, 4, 5)

val sum = numbers.fold(1000) { acc, number -> acc + number }
println(sum)  // Вывод: 1015

Функция sumBy

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

fun <T> Iterable<T>.sumBy(selector: (T) -> Int): Int

где

  • T — тип элементов исходной коллекции.
  • Int — тип результирующего значения.
  • selector — функция, которая принимает элемент типа исходной коллекции и возвращает целое число. Эта функция определяет, как преобразовать каждый элемент перед суммированием.

Примеры использования

Сумма длин строк

val strings = listOf("apple", "banana", "cherry")

val totalLength = strings.sumBy { it.length }
println(totalLength)  // Вывод: 17

Последовательности

Последовательности (Sequences) — это наборы данных, которые позволяют выполнять операции над своими элементами "отложенным" образом (lazy), т. е. по одному элементу за раз, только когда это необходимо. В отличие от обычных коллекций (списков, множеств и т.д.), которые выполняют все операции сразу, последовательности выполняют операции по мере их потребности, что может значительно повысить производительность, особенно при работе с большими наборами данных.

Основные характеристики последовательностей:

  1. Отложенность: Операции над последовательностями выполняются только тогда, когда это необходимо. Например, если вы выполняете фильтрацию и преобразование последовательности, эти операции будут выполняться по одному элементу за раз, а не для всей коллекции сразу.
  2. Однопроходность: Последовательности могут быть пройдены только один раз. После того как последовательность была полностью пройдена, она считается исчерпанной и не может быть использована снова.
  3. Отложенное выполнение: Результаты операций над последовательностями не вычисляются до тех пор, пока не будет вызвана функция, которая требует их (toList(), first(), forEach() и др.).

Создание последовательностей

Последовательности можно создавать несколькими способами:

  1. Из коллекции, используя метод asSequence().
  2. Из диапазона, используя функцию sequenceOf().
  3. Из генератора, используя функцию generateSequence().
// Создание последовательности из списка
val list = listOf(1, 2, 3, 4, 5)
val sequenceFromList = list.asSequence()

// Создание последовательности из диапазона
val sequenceFromRange = sequenceOf(1, 2, 3, 4, 5)

// Создание последовательности с использованием генератора
val sequenceFromGenerator = generateSequence(1) { it + 1 }

Операции над последовательностями

Последовательности поддерживают множество операций, аналогичных операциям над обычными коллекциями, таких как map, filter, flatMap, и др. Однако, в отличие от коллекций, эти операции выполняются "отложенным" образом.

Примеры операций над последовательностями

val numbers = sequenceOf(1, 2, 3, 4, 5)

// Фильтрация и преобразование
val evenSquares = numbers
    .filter { it % 2 == 0 }
    .map { it * it }

println(evenSquares.toList())  // Вывод: [4, 16]

// Взятие первых 3 элементов
val firstThree = numbers.take(3)
println(firstThree.toList())  // Вывод: [1, 2, 3]

// Пропуск первых 2 элементов
val skipTwo = numbers.drop(2)
println(skipTwo.toList())  // Вывод: [3, 4, 5]

Профилирование

Функция measureNanoTime

Функция measureNanoTime используется для измерения времени выполнения блока кода в наносекундах. Это особенно полезно при профилировании и оптимизации производительности кода, так как позволяет точно определить, сколько времени занимает выполнение конкретного участка кода.

inline fun measureNanoTime(block: () -> Unit): Long

где

  • block — функция, представляющая блок кода, время выполнения которого нужно измерить.

Примеры использования

Измерение времени выполнения функции суммирования

fun main() {
	fun sum() {  
		var sum = 0  
		for (i in 1..1000000) {  
			sum += i  
		}
	}
	
	val time = measureNanoTime { sum() }
	println("Время выполнения функции sum: $time наносекунд")
}

Измерение времени выполнения сортировки списка

fun main() {
    val list = (1..1000000).shuffled()

    val time = measureNanoTime {
        list.sorted()
    }
    println("Время выполнения сортировки: $time наносекунд")
}

Функция measureTimeMillis

Функция measureTimeMillis используется для измерения времени выполнения блока кода в миллисекундах. Это полезно при профилировании и оптимизации производительности кода, так как позволяет определить, сколько времени занимает выполнение конкретного участка кода.

inline fun measureTimeMillis(block: () -> Unit): Long

где

  • block — функция, представляющая блок кода, время выполнения которого нужно измерить.

Примеры использования

Измерение времени выполнения простого цикла

fun main() {
    val time = measureTimeMillis {
        var sum = 0
        for (i in 1..1000000) {
            sum += i
        }
    }
    println("Время выполнения: $time миллисекунд")
}

Измерение времени выполнения рекурсивной функции

fun factorial(n: Int): Int {
    return if (n == 1) 1 else n * factorial(n - 1)
}

fun main() {
    val time = measureTimeMillis {
        factorial(10)
    }
    println("Время выполнения факториала: $time миллисекунд")
}

Задание

Часть I

  • Возведение в степень элементов списка. Напишите программу, которая принимает список целых чисел и целое число n (степень), и возвращает новый список, в котором каждый элемент является результатом возведения соответствующего элемента исходного списка в степень n.
  • Преобразование списка строк в список их длин. Напишите программу, которая принимает список строк и возвращает список, содержащий длины этих строк.
  • Преобразование списка чисел в список их двоичных представлений. Напишите программу, которая принимает список целых чисел и возвращает новый список, в котором каждый элемент является двоичным представлением соответствующего элемента исходного списка.
  • Преобразование списка строк в список их уникальных символов. Напишите программу, которая принимает список строк и возвращает список, содержащий уникальные символы каждой строки.
  • Преобразование списка пар в список строк. Напишите программу, которая принимает список пар (Pair) строк и целых чисел и возвращает список строк вида "Строка: Число".
  • Преобразование списка троек в список произведений. Напишите программу, которая принимает список троек (Triple) целых чисел и возвращает список, содержащий произведения элементов каждой тройки.
  • Создание ассоциативного массива из списка пар. Напишите программу, которая преобразует список пар (Pair) вида (строка, целое число) в ассоциативный массив (Map), где ключом является строка, а значением — целое число.
  • Преобразование списка списков в список четных чисел. Напишите программу, которая преобразует этот список списков целых чисел в список четных чисел, содержащий все четные числа из всех списков.
  • Преобразование списка пар в список значений. Имеется список пар (Pair) вида (строка, список целых чисел). Напишите программу, которая преобразует этот список в список целых чисел, содержащий все элементы из всех списков.

Часть II

  • Фильтрация простых чисел. Напишите программу, которая принимает список целых чисел и возвращает новый список, содержащий только простые числа.
  • Фильтрация списка чисел на числа, делящиеся на 3 и 5. Напишите программу, которая принимает список целых чисел и возвращает новый список, содержащий только числа, которые делятся на 3 и на 5 одновременно.
  • Фильтрация строк, содержащих определенную подстроку. Напишите программу, которая принимает список строк и возвращает новый список, содержащий только строки, которые содержат заданную подстроку.
  • Фильтрация списка строк по регулярному выражению. Напишите программу, которая принимает список строк и возвращает новый список, содержащий только строки, которые являются адресами электронной почты.
  • Генерация последовательности Фибоначчи. Напишите программу, которая генерирует бесконечную последовательность чисел Фибоначчи и выводит первые 10 элементов.
  • Фильтрация и преобразование бесконечной последовательности. Напишите программу, которая генерирует бесконечную последовательность целых чисел, начиная с 1, фильтрует ее, оставляя только четные числа, удваивает каждый элемент и выводит первые 5 элементов результата.
  • Фильтрация и преобразование последовательности строк. Напишите программу, которая создает последовательность из списка строк, фильтрует ее, оставляя только строки, длина которых больше 5 символов, преобразует каждую строку в верхний регистр и выводит результат.
  • Комбинирование (объединение) списка строк и списка чисел. Напишите программу, которая принимает список строк и список целых чисел и возвращает новый список пар, где каждая пара состоит из строки и числа с одинаковыми индексами.
  • Комбинирование (объединение) списка дат и списка событий с преобразованием. Напишите программу, которая принимает список дат (объектов класса LocalDate) и список событий (строк) и возвращает новый список строк вида "Дата: Событие", где каждая строка состоит из даты и события с одинаковыми индексами.
  • Сумма элементов списка с использованием. Напишите программу, которая принимает список целых чисел и возвращает сумму всех элементов с использованием функции reduce.
  • Сумма цен товаров с использованием. Напишите программу, которая принимает список пар вида (наименование товара, цена) и возвращает сумму цен всех товаров с использованием функции sumBy.

Часть III

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

  1. Реализуйте три различных алгоритма сортировки:
    • Пузырьковая сортировка (Bubble Sort)
    • Сортировка слиянием (Merge Sort)
    • Быстрая сортировка (Quick Sort)
  2. Создайте функцию для генерации списка случайных чисел:
    • Функция должна принимать два параметра: размер списка и диапазон значений.
  3. Проведите профилирование каждого алгоритма сортировки:
    • Используйте функцию measureNanoTime для измерения времени выполнения в наносекундах.
    • Используйте функцию measureTimeMillis для измерения времени выполнения в миллисекундах.
    • Проведите профилирование для списков различных размеров (1000, 10000, 100000 элементов).
  4. Сравните результаты профилирования:
    • Выведите результаты в виде таблицы.
    • Проанализируйте, какой алгоритм является наиболее эффективным для каждого размера списка.
  5. Оптимизируйте один из алгоритмов сортировки:
    • Выберите один из алгоритмов и попытайтесь оптимизировать его.
    • Проведите повторное профилирование и сравните результаты с исходным алгоритмом.

Вопросы

  1. Преобразование данных
  2. Фильтрация данных
  3. Комбинирование данных
  4. Агрегирование данных
  5. Последовательности
  6. Профилирование

Ресурсы