0:00
[ЗВУК] [ЗВУК]
Тема сегодняшней лекции — функциональное программирование.
Ну и перед тем, как перейти непосредственно к функциональному
программированию, нужно понять, что такое парадигма программирования вообще.
Парадигма описывает общий подход к решению какой-то большой и сложной задачи.
И давайте посмотрим, какие они бывают.
Первая и наиболее знакомая нам — это императивное программирование.
Что это такое?
Мы пишем последовательность команд,
которые изменяют состояние нашей программы, изменяют какие-то переменные,
ну а декомпозиция задач осуществляется с помощью процедуры функций.
Примером так мы и писали наши программы.
У нас были, например, словари или какие-то другие объекты, списки,
отдельные переменные, мы обрабатывали их последовательностью команд.
Это вам хорошо знакомо, поэтому перейдем дальше.
Следующая парадигма — это декларативное программирование.
Вы не пишите, как добиться результата.
Вы ставите задачу и описываете, какой ответ вы хотите получить.
Звучит очень страшно, но на самом деле существует простой пример — это SQL,
или запросы к базе данных.
Вам неважно, как она устроена внутри, вы описываете структуру базы данных,
а потом какие данные вы хотите от нее получить.
Например, столбцы с какими-то условиями.
Вот неплохой пример декларативного программирования.
Следующая парадигма — это объектно-ориентированное программирование.
Когда оно появилось и расцвело в 80-х годах, казалось — всё, нашлась такая
«серебряная пуля», которая может решить все проблемы программирования, сейчас мы
все сделаем объектно-ориентированым и все будет хорошо.
Ну давайте посмотрим, что это такое.
Объектно-ориетированное программирование — это когда у нас в программе существуют
объекты, они живут своей жизнью, рождаются в результате вызова конструктора,
умирают в результате вызова деструктора.
Ну а на протяжении своей жизни обладают каким-то состоянием и могут
обмениваться информацией со всеми окружающими объектами,
посредством вызова публичных методов.
Подробнее об этом мы поговорим на следующей лекции,
а пока же перейдем к функциональному программированию.
Это достаточно экзотичная сейчас вещь.
Мало кто пишет в функциональном чистом стиле.
Но тем не менее у нее есть масса применений и,
в некоторых случаях, преимуществ.
Что же это такое?
В функциональном программировании большая задача разбивается на некоторые
элементарные функции, сквозь которые течет поток данных — data flow.
Таким образом, мы просто описываем какой-то небольшой кусочек,
небольшую простую функцию,
которая принимает на вход какие-то данные и возвращает какие-то данные.
Никакое общее состояние программы при этом не изменяется.
Такие задачи существуют.
Например, хотя бы даже взять потоковую обработку данных — видео или аудио.
В принципе, можно описать это в виде функциональной программы, где вот
этот поток (в нашем случае видео- или аудиоданных) течет сквозь разные функции.
Посмотрим, какие же есть
достоинства и недостатки функционального программирования.
Ну, недостатки серьезные.
Давайте начнем с достоинств, чтобы было понятно,
зачем же нам преодолевать все сложности.
Представьте себе, что у вас написана программа,
которая состоит только из вызова функций.
Ничего другого нет, даже переменные вы никакие не заводите, только то,
что передано в качестве параметров вы получили, а главное, ничего не меняете.
Вот это и будет такой радикально-функциональный стиль.
У вас совсем ничего императивного в ваших программах не останется.
Программу, написанную в функциональном стиле, очень легко доказать математически.
То есть если вы проводите какое-то рассуждение,
для функций это делается очень просто.
Функция — элементарная вещь, которая принимает данные и возвращает.
Команд в ней немного, и они тоже в свою очередь вызовы функций,
и математическое доказательство программы получается достаточно простым.
Какие-то другие парадигмы очень плохо подходят для доказательств.
Например, доказать корректность браузера — это практически неразрешимая задача.
Доказательство будет занимать гораздо больше, чем исходный код этого браузера.
Казалось бы, зачем доказывать алгоритмы?
Новые появляются редко, но даже сейчас,
когда расцвет этого доказательства корректности алгоритмов уже, в общем-то,
прошел, который был на заре их появления в 50–60-х годах,
тем не менее буквально несколько лет назад стандартная сортировка,
которая использовалась в Phyton, было доказано, что она неправильная.
Она сортировала раньше данные неправильно, ну и с помощью
такого доказательства подобрали конкретный пример, где она ломается.
Там было очень специфично, какие-то миллионы чисел,
тем не менее пришлось вносить исправления, чтобы это наконец-то стало правильным.
В функциональном программировании кроме доказательства,
также очень удобно проводить декомпозицию ряда задач.
Если ваша задача действительно разбивается на поток каких-то функций,
обрабатывающих данные, то вы, обладая некоторым опытом,
очень аккуратно нарежете на эти элементарные шаги функции,
и одна будет вызывать другую, и, в общем-то,
декомпозиция делается естественным образом по функциям с такими ограничениями.
Ну и наконец, если вы написали программу в функциональном стиле, то очень легко
ускорить ее выполнение, автоматически распараллелить ее тем или иным образом.
Что это такое?
Сейчас, как вы знаете,
в последнее время процессоры быстрее не начинают работать, хотя
буквально 10 лет назад все время росла тактовая частота и производительность.
Сейчас они растут в ширину, в количество ядер в основном, и задача стоит в том,
чтобы распараллелить программы.
Так вот, функциональные программы очень легко параллелить.
Если у вас есть функция от двух параметров,
вы один параметр считаете на одном ядре, второй — на втором ядре.
Примерно так это и делается.
Также там есть конвейеризация.
Что это такое?
Это мы на конвейер кладем.
Если мы хотим обработать с помощью функций вектор значений, то есть список, по сути,
и никак не зависеть от предыдущих значений операций, то мы может условно этот
списочек нарезать по количеству ядер и каждый кусочек отдать читать отдельно.
И также там возможно векторизация данных, когда мы параллельно
проводим некоторые вычисления или зацепляем операции одну за другую.
То есть мы посчитали какой-то результат выполнения умножения a * b + c, где всё
это вектора, и затем мы может посчитать кусочек произведения a * b, и уже
с этим обработанным кусочком параллельно начать складывать с вектором c.
Опять же в функциональной программе очень легко добиться того,
что все это будет происходить автоматически,
и человеку не нужно будет никаких действий совершать, чтобы все это распараллеливать.
Казалось бы, такая замечательная штука.
Почему все не пишут в функциональном стиле?
Главный недостаток функционального программирования — писать в функциональном
стиле достаточно сложно,
потому что приходится думать в очень непривычном формате.
И в самом простом случае, самая простая из проблем — что вам придется думать
в обратном порядке, то есть от результата спускаться к исходным данным.
Ну или писать справа налево.
Не всем это привычно.
Грубо говоря, вы сначала читаете что-то от входных данных,
затем от этого результата вызываете еще какую-то функцию и так далее.
Тем не менее приемы работы с векторами данных,
с массивами данных с помощью функций очень широко применяются
в стандартных библиотеках обработки данных и машинном обучении.
Мы на нашем курсе, конечно, не будем заниматься машинным обучением и будем
использовать только стандартные библиотеки языка Phyton, которые есть в поставке.
Но я постарался подобрать вам такие, которые будут наиболее похожи на то,
что вам может пригодиться в будущем для обработки больших объемов данных.
И сейчас мы перейдем к практике, где и посмотрим,
какие из стандартных функций для работы со списками,
с массивами данных каких-то, есть в языке Phyton.
Ну а чтобы вам было веселее, мы в задачах этого занятия ввели ограничения.
И решить их вы можете, только написав решения полностью в функциональном стиле.
[ЗВУК]
[ЗВУК]