2. Парадигми програмуваання
• Парадигма процедурного
програмування
• Парадигма об’єктно-орієнтованого
програмування
• Парадигма функційного програмування
• Парадигма реактивного програмування
4. lambda-expression
1. lambda-introducer ([]- не фіксуються, capture-default:
[&] — за посиланням, [=] — за значенням)
2. lambda declarator
Лямбда-вираз (або просто лямбда) в C++11 – спосіб визначення
анонімного об'єкта функції безпосередньо в місці його виклику або передачі
в функцію як аргумента. Зазвичай лямбда-вирази використовуються для
інкапсуляції декількох рядків коду, що передаються алгоритмам або
асинхронним методам.
6. std::future
std::future – забезпечує механізм доступу до
результатів асинхронних операцій:
• template<class T> class future; (1)( C++11)
• template<class T> class future<T&>; (2)( C++11)
• template<> class future<void>; (3)( C++11)
включає:
• (конструктор)
• (деструктор)
• operator= - переміщує майбутній результат
• share – повертає shared_future
• get – повертає результат
• valid
• wait
• wait_for
• wait_until
8. Парадигма функційного
програмування
• У функційному програмуванні програму
можна представити як обчислення
послідовності функцій, що не зберігають
ніяких станів. Тобто терміном функційне
програмування (ФП) називають такий стиль
програмування, при якому результат функції
залежить тільки від її параметрів, а не від
зовнішнього стану. Це прямо співвідноситься
з поняттям функції в математиці і означає, що
якщо два рази викликати функцію з одними і
тими ж параметрами, то вийдуть однакові
результати.
9. Haskell
• Haskell (укр. Гаскель, Гаскелл) — стандартизована, винятково
функційна мова програмування з нестрогою семантикою.
10. С++ і ФП
• C++ – мультипарадигменна мова, і на ній, можна писати
програми в стилі ФП.
Швидке сортування в стилі ФП
12. Послідовна імплементація
• Хоча інтерфейс витриманий в стилі ФП, пряме застосування ФП призвело б до
невиправдано великої кількості операцій копіювання, тому всередині ми використовуємо
«звичайний» імперативний стиль. В якості опорного ми вибираємо перший елемент і
відрізаємо його від списку за допомогою функції splice() (1). Потенційно це може привести
до неоптимального сортування (в термінах кількості операцій порівняння і обміну), але
будь-який інший підхід при роботі з std::list може істотно збільшити час за рахунок обходу
списку. Ми знаємо, що цей елемент повинен увійти в результат, тому можемо відразу
помістити його в список, де буде зберігатися результат. Далі ми хочемо використовувати
цей елемент для порівняння, тому беремо посилання на нього, щоб уникнути копіювання
(2). Тепер можна з допомогою алгоритму std::partition() розбити послідовність на дві
частини: менші опорного елементу і не менші опорного елементу (3). Критерій розбиття
найпростіше задати за допомогою лямбда-функції. Тут ми запам'ятовуємо посилання в
замиканні, щоб не копіювати значення pivot.
• Алгоритм std::partition() переупорядковує список на місці і повертає ітератор, який вказує на
перший елемент, що не менший опорного значення. Повний тип ітератора досить довгий,
тому використовуємо специфікатор типу auto, щоб компілятор вивів його самостійно.
• Оскільки дотримуємося парадигми ФП, то для рекурсивного сортування обох «половин»
потрібно створити два списки. Для цього ми знову використовуємо функцію splice(), щоб
перемістити значення зі списку input включно до divide_point в новий список lower_part (4).
Після цього input буде містити тільки всі інші значення. Далі обидва списки можна
відсортувати шляхом рекурсивних викликів (5), (6). Застосовуючи std::move() для передачі
списків, ми уникаємо копіювання – результат в будь-якому випадку неявно переміщується.
Нарешті, ми ще раз викликаємо splice(), щоб скласти result в правильному порядку.
Значення зі списку new_higher потрапляють в кінець списку (7), після опорного елементу, а
значення зі списку new_lower – в початок списку, до опорного елемента (8).
14. Паралельна імплементація
• Істотна зміна тут полягає в тому, що сортування у нижній частині списку проводиться не в поточному, а в
окремому потоці – за допомогою std::async() (1). Верхня частина списку, як і раніше, сортується шляхом
прямої рекурсії (2). Рекурсивно викликаючи parallel_quick_sort(), ми можемо задіяти доступний апаратний
паралелізм. Якщо std::async() створює новий потік при кожному зверненні, то після трьох рівнів рекурсії ми
отримаємо вісім працюючих потоків, а після 10 рівнів (коли в списку приблизно 1000 елементів) буде
працювати 1024 потоки, якщо це апаратно підтримується. Якщо бібліотека вирішить, що запущено занадто
багато завдань (приміром, тому що кількість завдань перевищила рівень апаратного паралелізму), то може
перейти в режим синхронного запуску нових завдань. Тоді нове завдання буде працювати в тому ж потоці,
який звернувся до get(), а не в новому, так що ми не будемо нести витрати на передачу завдання новому
потоку. Варто зазначити, що відповідно до стандарту реалізація std::async вправі як створювати новий потік
для кожного завдання (навіть при значному перевищенні ліміту), якщо явно не заданий прапорець
std::launch::deferred, так і запускати всі завдання синхронно, якщо явно не заданий прапоруць
std::launch::async.
• Можна не використовувати std::async(), а написати свою функцію spawn_task(), яка буде служити обгорткою
навколо std::packaged_task і std::thread, як показано в лістингу 2.3; потрібно створити об'єкт
std::packaged_task для зберігання результату виклику функції, отримати з нього майбутній результат,
запустити завдання в окремому потоці і повернути майбутній результат. Само по собі це не дає великої
переваги (і, швидше за все, призведе до значного перевищення ліміту), але прокладає дорогу до переходу
на більш хитромудру реалізацію, яка поміщає завдання в чергу, яка обслуговується пулом потоків.
Розглядати пули потоків не будемо, крім того, йти по такому шляху замість використання std::async має
сенс тільки в тому випадку, коли ви точно знаєте, що робите, і хочете повністю контролювати, як пул
потоків будується і виконує завдання.
• Повернемося до функції parallei_quick_sort. Оскільки для отримання new_higher ми застосовували пряму
рекурсію, то і склеїти (splice) його можна на місці, як і раніше (3). Але new_lower тепер представляє собою
не список, а об'єкт std::future<std::list<T>>, тому спочатку потрібно витягти значення за допомогою get(), а
тільки потім викликати splice() (4). Таким чином, ми дочекаємося завершення фонового завдання, а потім
перемістимо результат в параметр splice(); функція get() повертає посилання на r-значення збереженого
результату, отже, його можна перемістити.
18. Парадигма реактивного
програмування
• Реактивне програмування – парадигма
програмування, що побудована на потоках даних та
взаємному розповсюдженні змін.
• Наприклад, у нас є наступний вираз: int sum = a + b .
В імперативному стилі це означає, що sum отримує
значення a + b безпосередньо під час обчислення
виразу і якщо після обчислення значення змінних
зміняться, то це вже ніяк не вплине на обчислений
результат. Проте, в реактивному програмуванні
значення sum буде автоматично оновлено,
якщо a і b зміняться (навіть після обчислення
результату).
20. React
(ReactJS)
• React(старі назви: React.js, ReactJS) — відкрита JavaScript бібліотека
для створення інтерфейсів користувача, яка покликана вирішувати
проблеми часткового оновлення вмісту веб-сторінки, з якими
стикаються в розробці односторінкових застосунків. Розробляється
Facebook, Instagram і спільнотою індивідуальних розробників.
• React дозволяє розробникам створювати великі веб-застосунки, які
використовують дані, котрі змінюються з часом, без перезавантаження
сторінки. Його мета полягає в тому, щоб бути швидким, простим,
масштабованим. React обробляє тільки користувацький інтерфейс у
застосунках. Це відповідає видові у шаблоні модель-вид-контролер
(MVC), і може бути використане у поєднанні з іншими JavaScript
бібліотеками або в великих фреймворках MVC, таких як AngularJS. Він
також може бути використаний з React на основі надбудов, щоб
піклуватися про частини без користувацького інтерфейсу побудови веб-
застосунків.
• В даний час React використовують Khan Academy, Netflix, Yahoo,
Airbnb, Sony, Atlassian та інші.
21. React
(ReactJS)
• Одностороння передача даних. Властивості передаються в рендерер
компоненту, як властивості html тега. Компонент не може напряму змінювати
властивості, що йому передані, але може їх змінювати через callback функції.
Такий механізм називають «властивості донизу, події нагору».
• Віртуальний DOM. React підтримує віртуальний DOM, а не покладається
виключно на DOM браузера. Це дозволяє бібліотеці визначити, які частини DOM
змінилися, порівняно (diff) зі збереженою версією віртуального DOM, і таким
чином визначити, як найефективніше оновити DOM браузера. Таким чином
програміст працює зі сторінкою, вважаючи що вона оновлюється вся, але
бібліотека самостійно вирішує які компоненти сторінки треба оновити.
• JSX. Компоненти React зазвичай написані на JSX. Код написаний на JSX
компілюється у виклики методів бібліотеки React. Розробники можуть так само
писати на чистому JavaScript. JSX нагадує іншу мову, яку створили у компанії
Фейсбук для розширення PHP, XHP.
• Не лише рендер HTML в браузері. React використовують не лише для
рендерингу HTML в браузері. Наприклад, Facebook має динамічні графіки які
рендеряться в теги <canvas>, Netflix та PayPal використовують ізоморфне
завантаження для рендерингу ідентичного HTML на сервері та клієнті.
25. Модель взаємодії
• Observable – об’єкт чи функція, що повертає послідовність даних
• Observer – об’єкт чи функція, що реалізує обробку
• Subscriber – об’єкт чи функція, що зв’язує Observable і Observer
26. RxJava
• RxJava – реактивні розширення (Rx) для JVM, що
дозволяють писати асинхронні побудовані на подіях
програми, икористовуючи observable послідовності.
Тому для початку нам потрібно створити
простий Observable:
27. RxJava
• Тепер створимо Subscriber для того, щоб прийняти і
опрацювати дані:
Є 3 основні методи:
• onNext – викликається при отриманні нового значення;
• onCompleted – вказує, що робити при завершенні;
• onError – опрацювання помилок.
30. RxJava
• Події в Rx описуються в такому ж стилі, як і в бібліотеках для
функціонального програмуванням, наприклад, Java Streams. Rx
дає можливість використовувати функціональні трансформації
над потоками подій.
• RxJava може бути розширена для користувача операторами. І
хоча Java не дозволяє перевантажувати свої оператори, RxJava
пропонує всю функціональність доступну в реалізаціях Rx на
будь-якій іншій мові.
• Функціональні трансформації оголошені декларативно.
• Оператори в Rx легко компонуються, щоб проводити складні
операції.
• Оператори в Rx можуть трансформувати типи даних,
фільтруючи, обробляючи й розширюючи потоки даних при
необхідності.
44. План
• Лек.13.1 Нормальні алгоритми Маркова
• Лек.13.2 Машина Тюрінга
• Лек.14.1 Огляд теорії кінцевих автоматів
• Лек.14.2 Теорія формальних мов
45. Алгоритмічні ситеми
• Нехай Α= < φ ,Ρ > - деякий алгоритм із
системою правил Р .
• Алгоритм АF =< φ, PF >, де PF — формальний
опис системи Р , будемо називати
формальним еквівалентом алгоритму А.
• Алгоритмічною системою називають
спосіб задання алгоритмів у деякому
фіксованому формалізмі F, який дає змогу
для довільного наперед заданого алгоритму
А задати його формальний еквівалент АF.
46. Алгоритмічні системи
(Формальні алгоритмічні системи)
Алгоритмічні системи можна вважати формалізацією поняття
алгоритму. Виділяють три типи таких систем, які відрізняються
початковими евристичними міркуваннями стосовно того, що
таке алгоритм:
– у першому типі поняття алгоритму пов’язане з найтрадиційнішими
поняттями математики - обчисленнями та числовими функціями
(числовою називають функцію, значення якої та значення її
аргументів - невід’ємні числа). Рекурсивні функції - історично
перша формалізація поняття алгоритму;
– другий тип ґрунтується на уявленні про алгоритм як
детермінований пристрій, здатний виконувати в кожний окремий
момент лише дуже примітивні операції. Основна теоретична
система цього типу - машина Тьюрінга;
– третій тип алгоритмічних систем — це перетворення слів у
довільних алфавітах. Приклади систем цього типу – канонічна
система Поста й нормальні алгоритми Маркова.
47. Нормальні алгоритми Маркова
• Для формалізації поняття алгоритму А. Марков 1954 р. розробив систему
нормальних алгоритмів. Алгоритми Маркова — це формальна математична
система. Вони були основою для першої мови обробки рядів СОМІТ. Крім того, є
подібність між моделлю Маркова і мовою СНОБОЛ, яка з’явилась після СОМІТ.
• Простою продукцією (формулою підстановки) називають запис вигляду:
u —> w,
де и, w - рядки в алфавіті V . У цьому разі V не містить символів ‘->' та ‘.‘.
Величину и називають ангицедентом , a w - консеквентом.
• Вважають, що формула и —> w може бути застосована до рядка Z є F , якщо є
хоча б одне входження и в Z. В іншому випадку ця формула не застосовна до
рядка Z. Якщо формула може бути застосована, то канонічне (перше ліворуч)
входження и в Z замінюється на w. Наприклад, якщо формулу 'ba' —>’c'
застосувати до вхідного рядка 'ababab' , то в підсумку отримаємо рядок
'achab'. Проте формула 'baa' —> 'c' до рядка 'ababab' не може бути
застосована.
• Заключною продукцією (заключною підстановкою) називають запис вигляду
и —> -w, де u,w - рядки в V.
• Упорядковану множину продукцій Р1,Р7,...,Рn називають
нормальним алгоритмом, чи алгоритмом Маркова.
48. Нормальні алгоритми Маркова
П р и к л ад.
• Нехай над словами з алфавіту V = {а,Ь,с} задано алгоритм з
такими формулами підстановки
– Р1 : 'аb' —> 'b',
– Р2 : ’ас' —> 'с',
– Р3 : 'aa' —> 'a'.
• Цей алгоритм вилучає всі входження символа 'a' з рядка, за
винятком випадку, коли 'а‘ є в кінці рядка.
• Простежимо роботу алгоритму, якщо вхідний рядок має вигляд
'bacaabaa'. Нехай символ => означає результат перетворення, а
підрядок, який підлягає заміні, будемо підкреслювати:
'bacaabaa' => 'bacabaa' => 'bacbaa' => 'bcbaa ' => 'bcba'.
Р1 Р1 Р2 Р3
• Оскільки далі жодна з формул не може бути застосована, то на
цьому робота алгоритму завершується.
49. Машина Тюрінга
• На змістовному рівні машина Тьюрінга (МТ) є деякою
гіпотетичною(умовною) машиною, яка складається з
трьох головних компонент: інформаційної стрічки,
головки для зчитування і запису та пристрою
керування.
50.
51. Алгоритмічна система Тюрінга
Модель однострічкової детермінованої МТ задається шісткою(сімкою):
М = < A, I, Q, q0, qf, a0, p >,
де A – кінцева множина символів зовнішнього алфавіту,
I – кінцева множина символів зовнішнього алфавіту на стрічці,
Q – кінцева множина символів внутрішнього алфавіту,
q0 – початковий стан,
qf – кінцевий стан,
q0, qf Є Q
a0 – позначення порожньої комірки стрічки,
p – така програма, яка не може мати двох команд, у яких би збігалися
два перші символи:
{A}x{Q} {A}{L,R,S}{Q},
де L – зсувати головку вліво,
R – зсувати головку вправо,
S – головка залишається на місці.