├── README.md ├── assets ├── LMAP_August_research.jpg ├── application.jpg ├── basics.jpg ├── binaries.jpg ├── blog.css ├── blogit.png ├── blueprints.jpg ├── control_flow.jpg ├── elixir_header_logo.png ├── elixir_most_important_part.png ├── erlang_ad_hoc_implement.png ├── explosion.jpg ├── flow.jpg ├── generic_communication.jpeg ├── git_logo.png ├── input_output.gif ├── internals.png ├── keys.jpg ├── links.jpg ├── lists.jpg ├── logo.png ├── metaprogramming.jpg ├── mix_test_output.png ├── mixing_tools.jpg ├── modules.jpg ├── parallel.jpg ├── process.png ├── schedulers_now.png ├── spiral.png ├── streams.jpg ├── strings.jpg ├── structs.jpg ├── supervisor.jpg ├── tasks.png ├── template.jpg ├── tuples.jpg ├── why_elixir.jpg └── wrapper.jpg ├── blog.yml ├── old_news_and_organisation_posts ├── 2018-2020 │ ├── exam_schedule.md │ ├── exam_schedule_august.md │ ├── exam_schedule_september.md │ ├── project_2019.md │ ├── task_one_2019.md │ ├── task_three_2019.md │ └── task_two_2019.md ├── august_exam.md ├── blogit_1_0_1.md ├── exam_v1.1.0_schedule.md ├── news_exam.md ├── news_moving.md ├── news_no_entry_test.md ├── news_pre_exam.md ├── project.md ├── project_one.md ├── project_one_presentation.md ├── register.md ├── resources.md ├── schedule_eight_of_may.md ├── schedule_exam.md ├── summary.md ├── summer_exam_2018.md ├── task_four.md ├── task_one.md ├── task_one_2018.md ├── task_three.md ├── task_two.md ├── task_two_2019.md └── weekly_schedule.md └── posts ├── archive ├── application.md ├── binaries.md ├── control_flow.md ├── exceptions.md ├── input_output.md ├── lists_streams_recursion.md ├── maps_structs.md ├── metaprogramming_part1.md ├── metaprogramming_part2.md ├── modules_functions_recursion.md ├── pattern_matching_types_and_basics.md ├── process_internals.md ├── process_links.md ├── processes.md ├── processes_and_state.md ├── protocols.md ├── strings.md ├── supervision.md ├── tasks.md ├── types_and_behaviours.md └── why_elixir.md ├── en └── why_elixir.md ├── materials ├── binaries.md ├── concurrency_processes.md ├── concurrency_tasks_agents.md ├── enum_and_stream.md ├── exceptions_and_io.md ├── gen_server.md ├── how_to_git.md ├── lists.md ├── maps.md ├── metaprogramming_part1.md ├── metaprogramming_part2.md ├── mix_tool.md ├── modules_and_functions.md ├── pattern_matching_and_control_flow.md ├── process_internals.md ├── process_links.md ├── protocols.md ├── strings.md ├── structs.md ├── tuples_and_keyword_lists.md ├── types.md └── why_elixir.md └── news ├── first_homework.md ├── fourth_homework.md ├── second_homework.md ├── summary.md └── third_homework.md /README.md: -------------------------------------------------------------------------------- 1 | # elixir-lang.bg blog 2 | 3 | The blogposts shown on https://blog.elixir-lang.bg 4 | -------------------------------------------------------------------------------- /assets/LMAP_August_research.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/LMAP_August_research.jpg -------------------------------------------------------------------------------- /assets/application.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/application.jpg -------------------------------------------------------------------------------- /assets/basics.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/basics.jpg -------------------------------------------------------------------------------- /assets/binaries.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/binaries.jpg -------------------------------------------------------------------------------- /assets/blog.css: -------------------------------------------------------------------------------- 1 | div.container { 2 | } 3 | 4 | header.header { 5 | background: #563c76; 6 | color: white; 7 | display: flex; 8 | height: 155px; 9 | margin-bottom: 0; 10 | } 11 | 12 | div.titles { 13 | padding-top: 10px; 14 | } 15 | 16 | div.blog-logo { 17 | padding: 16px; 18 | float: left; 19 | } 20 | 21 | @media screen and (max-width: 768px) { 22 | header.header { 23 | height: 113px; 24 | } 25 | 26 | div.titles { 27 | padding-top: 14px; 28 | } 29 | 30 | div.blog-logo img { 31 | height: 80px; 32 | } 33 | 34 | .blog-title { 35 | font-size: 36px; 36 | } 37 | 38 | .blog-sub { 39 | display: none; 40 | } 41 | 42 | div.blog-logo { 43 | padding-left: 0px; 44 | } 45 | } 46 | 47 | @media screen and (max-width: 400px) { 48 | header.header { 49 | height: 83px; 50 | } 51 | 52 | div.titles { 53 | padding-top: 8px; 54 | } 55 | 56 | div.blog-logo img { 57 | height: 50px; 58 | } 59 | 60 | .blog-title { 61 | font-size: 24px; 62 | } 63 | 64 | .blog-sub { 65 | display: none; 66 | } 67 | 68 | div.blog-logo { 69 | padding-left: 0px; 70 | } 71 | } 72 | 73 | img.inline-image { 74 | width: 100%; 75 | height: auto; 76 | } 77 | 78 | .main-menu { 79 | margin-bottom: 30px; 80 | } 81 | 82 | .main-menu .nav-pills > li.active > a { 83 | background: #563c76; 84 | color: white; 85 | } 86 | 87 | .main-menu .nav-pills > li > a { 88 | color: #563c76; 89 | } 90 | -------------------------------------------------------------------------------- /assets/blogit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/blogit.png -------------------------------------------------------------------------------- /assets/blueprints.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/blueprints.jpg -------------------------------------------------------------------------------- /assets/control_flow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/control_flow.jpg -------------------------------------------------------------------------------- /assets/elixir_header_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/elixir_header_logo.png -------------------------------------------------------------------------------- /assets/elixir_most_important_part.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/elixir_most_important_part.png -------------------------------------------------------------------------------- /assets/erlang_ad_hoc_implement.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/erlang_ad_hoc_implement.png -------------------------------------------------------------------------------- /assets/explosion.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/explosion.jpg -------------------------------------------------------------------------------- /assets/flow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/flow.jpg -------------------------------------------------------------------------------- /assets/generic_communication.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/generic_communication.jpeg -------------------------------------------------------------------------------- /assets/git_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/git_logo.png -------------------------------------------------------------------------------- /assets/input_output.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/input_output.gif -------------------------------------------------------------------------------- /assets/internals.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/internals.png -------------------------------------------------------------------------------- /assets/keys.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/keys.jpg -------------------------------------------------------------------------------- /assets/links.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/links.jpg -------------------------------------------------------------------------------- /assets/lists.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/lists.jpg -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/logo.png -------------------------------------------------------------------------------- /assets/metaprogramming.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/metaprogramming.jpg -------------------------------------------------------------------------------- /assets/mix_test_output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/mix_test_output.png -------------------------------------------------------------------------------- /assets/mixing_tools.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/mixing_tools.jpg -------------------------------------------------------------------------------- /assets/modules.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/modules.jpg -------------------------------------------------------------------------------- /assets/parallel.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/parallel.jpg -------------------------------------------------------------------------------- /assets/process.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/process.png -------------------------------------------------------------------------------- /assets/schedulers_now.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/schedulers_now.png -------------------------------------------------------------------------------- /assets/spiral.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/spiral.png -------------------------------------------------------------------------------- /assets/streams.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/streams.jpg -------------------------------------------------------------------------------- /assets/strings.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/strings.jpg -------------------------------------------------------------------------------- /assets/structs.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/structs.jpg -------------------------------------------------------------------------------- /assets/supervisor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/supervisor.jpg -------------------------------------------------------------------------------- /assets/tasks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/tasks.png -------------------------------------------------------------------------------- /assets/template.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/template.jpg -------------------------------------------------------------------------------- /assets/tuples.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/tuples.jpg -------------------------------------------------------------------------------- /assets/why_elixir.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/why_elixir.jpg -------------------------------------------------------------------------------- /assets/wrapper.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElixirCourse/blog/0df75e104f8acdf05d505db4dc13458aed8a9c28/assets/wrapper.jpg -------------------------------------------------------------------------------- /blog.yml: -------------------------------------------------------------------------------- 1 | title: Курс по Elixir 2 | sub_title: Статии по темите преподавани в курса по Elixir във ФМИ 3 | logo_path: elixir_header_logo.png 4 | styles_path: blog.css 5 | social: 6 | rss: true 7 | stars_for_blogit: true 8 | en: 9 | title: FMI Elixir Course 10 | sub_title: The site/blog of the Bulgarian Elixir Course at FMI 11 | archive: 12 | title: Курс по Elixir (Архив) 13 | sub_title: Статии по темите от курса по Elixir във ФМИ (2017 година) 14 | -------------------------------------------------------------------------------- /old_news_and_organisation_posts/2018-2020/exam_schedule.md: -------------------------------------------------------------------------------- 1 | --- 2 | title_image_path: LMAP_August_research.jpg 3 | category: Изпит 4 | author: ivan 5 | pinned: true 6 | tags: 7 | - elixir 8 | - exam 9 | --- 10 | 11 | # Разписание за изпит 12 | 13 | Разписанието за изпита утре (събота, 06.07.2019) е както следва: 14 | 15 | ``` 16 | | Час | Meddle | Слави | Иван | 17 | |-------|-------------------|--------------------|--------------------| 18 | | 13:00 | Атанас Орманов | Христоско Чаушев | Даниел Семков | 19 | | 13:30 | Емилиан Станков | Здравко Георгиев | Милка Вълканова | 20 | | 14:00 | Антон Чернев | Александър Лазаров | Добромир Иванов | 21 | | 14:30 | Радослав Георгиев | Йоана Стоянова | Ростеслав Йорданов | 22 | ``` 23 | 24 | Ако има някой изпуснат може да ни пише или да дойде след 14:00. 25 | Ако някой няма да се яви - нека също ни съобщи, за да не го чакаме. 26 | Тъй като е много вероятно изпитването при някои да продължи по-малко от 30 минути, моля идвайте с поне 15 минути по-рано. 27 | 28 | Залата е **314**. 29 | 30 | ## After Party 31 | 32 | Както традицията повелява вечерта след изпита правим афтър парти в бар [Ателието](https://www.facebook.com/pages/category/Bar/Ателието-1448528298699274/), което започва след 19:00 (+/- 30 минути). Поканени са всички взели и невзели изпита, хора от минали години или просто хора, които са видели този пост. 33 | -------------------------------------------------------------------------------- /old_news_and_organisation_posts/2018-2020/exam_schedule_august.md: -------------------------------------------------------------------------------- 1 | --- 2 | title_image_path: LMAP_August_research.jpg 3 | category: Изпит 4 | pinned: true 5 | tags: 6 | - elixir 7 | - exam 8 | --- 9 | 10 | # Изпит, поправителен 11 | 12 | Изпитът ще е на 31-ви Август, зала 013 13 | 14 | Трябва да ни предложите проект в email, даже и да сте предлагали преди това. 15 | Описанието да е по-детайлно, не едно изречение. Пишете на course@elixir-lang.bg 16 | 17 | Ако не искате да си ползвате точките от тази година, ще ви изпитаме на място. 18 | -------------------------------------------------------------------------------- /old_news_and_organisation_posts/2018-2020/exam_schedule_september.md: -------------------------------------------------------------------------------- 1 | --- 2 | title_image_path: LMAP_August_research.jpg 3 | category: Изпит 4 | pinned: true 5 | tags: 6 | - elixir 7 | - exam 8 | --- 9 | 10 | # Изпит, поправителен 11 | 12 | Изпитът ще е на 12-ти Септември, зала 306, от 10:00 часа. 13 | 14 | Трябва да ни предложите проект в email, даже и да сте предлагали преди това. 15 | Описанието да е по-детайлно, не едно изречение. Пишете на course@elixir-lang.bg 16 | -------------------------------------------------------------------------------- /old_news_and_organisation_posts/2018-2020/project_2019.md: -------------------------------------------------------------------------------- 1 | --- 2 | title_image_path: LMAP_August_research.jpg 3 | category: Програма 4 | created_at: 2019-05-24T23:35:12 5 | author: Reductions, ivan 6 | pinned: true 7 | tags: 8 | - elixir 9 | --- 10 | 11 | # Инструкции за финален проект 12 | 13 | Припомняме скалата за финалната оценка, която беше показана в началото на курса: 14 | 15 | | Точки | 2 | 3 | 4 | 5 | 6 | 16 | | :----: | :--: | :---: | :---: | :---: | :--------: | 17 | | Оценка | 0-49 | 50-57 | 58-74 | 75-91 | 92-∞ | 18 | 19 | {: .table .table-striped .table-bordered .table-hover} 20 | 21 | Разпределението на точките е както следва: 22 | 23 | - Домашни - 45 точки 24 | - Финален проект - 40-80 точки 25 | 26 | Максималният възможен брой точки е 125 (без да броим бонус точките, давани за активно участие в лекциите). 27 | 28 | Единствените условия, за да завършите курса по Elixir са: 29 | 30 | - Да имате поне 50 точки 31 | - Да сте получили поне 20 от проекта 32 | 33 | ## Предлагане на проект 34 | 35 | Трябва да предложите тема за проект до 23:59 на 07.06.2019 (всеки проект предложен след тази дата остава за Септември). Предложението за проект трябва да е кратко и ясно, описващи функционалността на проекта Ви, изпратени до **course@elixir-lang.bg**. В рамките на ден ще ви отговорим дали одобряваме проекта и колко точки максимум би ви донесъл той (40, 50, 60, 70 или 80). 36 | 37 | ## Оценяване на проекта 38 | 39 | Точките от проекта имат следното разпределение: 40 | 41 | - 10 точки ще бъдат давани а добър функционален стил 42 | - 30% от максималния брой точки ще са за тестове и документация 43 | - останалите точки са за пълнота и функционалност на проекта ви. 44 | 45 | Ето как изглежда разбиването на точките за проектите от различните категории. 46 | 47 | | максимални точки | стил | тестове | функционалност | 48 | | :--------------: | :--: | :-----: | :------------: | 49 | | 40 | 10 | 12 | 18 | 50 | | 50 | 10 | 15 | 25 | 51 | | 60 | 10 | 18 | 32 | 52 | | 70 | 10 | 21 | 39 | 53 | | 80 | 10 | 24 | 46 | 54 | 55 | {: .table .table-striped .table-bordered .table-hover} 56 | 57 | ## Класификация на проектите 58 | 59 | ### Проект за 40 точки 60 | 61 | Това е проект, който реализира програма, изпълняваща се в един процес. 62 | 63 | ### Проект за 50 точки 64 | 65 | Проект за 50 точки трябва да има поне минимална паралелизация. 66 | 67 | ### Проект за 60 точки 68 | 69 | Трябва да направите **OTP application** с добре направено супервайзор дърво. 70 | 71 | ### Проект за 70 точки 72 | 73 | По-голям и приложно ориентиран **OTP application** (обикновено това е проект, който използва или предоставя някакви ресурси онлайн). 74 | 75 | ### Проект за 80 точки 76 | 77 | Много рядко бихме дали 80 точки. Това са проекти, които попадат в някоя от тези категории: 78 | 79 | - Библиотека, която да е полезна за много хора (и няма известни или добри решения) 80 | - Проект, в който има солидно (и интелигентно) мета програмиране 81 | - Дистрибутирана система, която наистина има смисъл да бъде такава. 82 | 83 | Когато изпратите мейл с предложение, то ще получите отговор в който: 84 | 85 | - Одобряваме или не одобряваме вашата идея 86 | - В случай че харесаме вашето предложение може да предложим промени по идеята, които смятаме за удачни 87 | - Максимален брой точки, които тази проект може да получи 88 | 89 | ## Представяне на проекти 90 | 91 | Ще се състои на 6-и Юли. Когато наближи датата ще обявим график за представяне. Ако някой от вас смята, че тази дата е твърде късна за него или нея, нека го отбележи в имейла с предложението за проект. Ако има желаещи за по-ранна дата ще насрочим втора, по-ранна, дата за защита. 92 | -------------------------------------------------------------------------------- /old_news_and_organisation_posts/2018-2020/task_one_2019.md: -------------------------------------------------------------------------------- 1 | --- 2 | category: Програма 3 | created_at: 2019-03-19T15:55:00 4 | tags: 5 | - elixir 6 | - homework 7 | --- 8 | 9 | # Първо домашно по Elixir 10 | 11 | ### Предварителни изисквания 12 | 13 | Трябва да сте влезли в своя [github](https://github.com) профил. Ако нямате - трябва да си създатете. 14 | 15 | ### Получаване на домашно 16 | 17 | За да получите първото домашно последвайте следния [линк](https://classroom.github.com/a/rjVY3sIy) и натиснете бутона `Accept this assignment`. 18 | След като приемете, то ще получите достъп за писане и четене до частно хранилище, което се намира в `ElixirCourse` организацията и има името `task-one-2019-<вашето гитхъб име>`. В него ще намерите условието на задачата. 19 | 20 | ### Краен срок 21 | 22 | Крайният срок е 01.04.2019г., 23:59. Когато той изтече, правата ви за писане в хранилището ще бъдат автоматично отнети и кодът, който е наличен в master, ще бъде оценен. 23 | 24 | Успех! 25 | -------------------------------------------------------------------------------- /old_news_and_organisation_posts/2018-2020/task_three_2019.md: -------------------------------------------------------------------------------- 1 | --- 2 | category: Програма 3 | tags: 4 | - elixir 5 | - homework 6 | --- 7 | 8 | # Трето домашно по Elixir 9 | 10 | ### Предварителни изисквания 11 | 12 | Трябва да сте влезли в своя [github](https://github.com) профил. Ако нямате - трябва да си създатете. 13 | 14 | ### Получаване на домашно 15 | 16 | За да получите третото домашно последвайте следния [линк](https://classroom.github.com/a/pHORzm3P) и натиснете бутона `Accept this assignment`. 17 | След като приемете, то ще получите достъп за писане и четене до частно хранилище, което се намира в `ElixirCourse` организацията и има името `task-three-2019-<вашето гитхъб име>`. В него ще намерите условието на задачата. 18 | 19 | ### Краен срок 20 | 21 | Крайният срок е 20.05.2019 г., 23:59. Когато той изтече, правата ви за писане в хранилището ще бъдат автоматично отнети и кодът, който е наличен в master, ще бъде оценен. 22 | 23 | Успех! 24 | -------------------------------------------------------------------------------- /old_news_and_organisation_posts/2018-2020/task_two_2019.md: -------------------------------------------------------------------------------- 1 | --- 2 | category: Програма 3 | created_at: 2019-03-19T15:55:00 4 | tags: 5 | - elixir 6 | - homework 7 | --- 8 | 9 | # Второ домашно по Elixir 10 | 11 | ### Предварителни изисквания 12 | 13 | Трябва да сте влезли в своя [github](https://github.com) профил. Ако нямате - трябва да си създатете. 14 | 15 | ### Получаване на домашно 16 | 17 | За да получите първото домашно последвайте следния [линк](https://classroom.github.com/a/qv-3JXxf) и натиснете бутона `Accept this assignment`. 18 | След като приемете, то ще получите достъп за писане и четене до частно хранилище, което се намира в `ElixirCourse` организацията и има името `task-two-2019-<вашето гитхъб име>`. В него ще намерите условието на задачата. 19 | 20 | ### Краен срок 21 | 22 | Крайният срок е 02.05.2019г., 23:59. Когато той изтече, правата ви за писане в хранилището ще бъдат автоматично отнети и кодът, който е наличен в master, ще бъде оценен. 23 | 24 | Успех! 25 | -------------------------------------------------------------------------------- /old_news_and_organisation_posts/august_exam.md: -------------------------------------------------------------------------------- 1 | category: Новини 2 | tags: 3 | - organization 4 | - course 5 | - exam 6 | 7 | -------- 8 | 9 | # Дата, час, място и условия за поправителен изпит 10 | 11 | Изпитът е на 27-ми Август, Неделя от 10 часа сутринта, 311-та зала. 12 | 13 | До 27-ми Юли очакваме предложения за проекти, знаете процедурата. 14 | Ако искате да правите проект, който сте ни предложили за 8-ми Юли, пак ни пишете мейл, в който да ни кажете това. 15 | 16 | На изпита ще имате избор: 17 | * Да си запазите точките от домашните и да ви оценим с точки-от-проект + точки-от-домашни 18 | * Да се откажете от точките от домашните. Ще имате възможност да изкарате до 40 точки. Ще ви зададем 8 въпроса за по 5 точки, които да ни имплементирате на място. 19 | 20 | Няма да има друга дата за поправка. 21 | -------------------------------------------------------------------------------- /old_news_and_organisation_posts/blogit_1_0_1.md: -------------------------------------------------------------------------------- 1 | category: Blogit 2 | tags: 3 | - elixir 4 | - release 5 | - 1.0.1 6 | - blogit 7 | - blog 8 | 9 | -------- 10 | 11 | # Blogit 1.0.1 Released 12 | 13 | Blog engine-а, който ползваме за този блог вече е в доста добра форма. 14 | Има си документация, която можете да намерите [тук](https://hexdocs.pm/blogit/1.0.1/) и ще бъде разработван все повече и повече. 15 | 16 | За да го support-нете, можете да му дадете звездичка или да го fork-нете от repository-то му : [https://github.com/meddle0x53/blogit](https://github.com/meddle0x53/blogit) 17 | 18 | Същото можете да направите и за BlogitWeb ([https://github.com/meddle0x53/blogit_web](https://github.com/meddle0x53/blogit_web)), Phoenix application-ът, който го ползва. 19 | 20 | Всъщност най-голяма подкрепа би била да ми помогнете с работата по него : с issue-та, с PR-и, с каквото се сетите, а защо не и да си направите вашия блог с Blogit? 21 | -------------------------------------------------------------------------------- /old_news_and_organisation_posts/exam_v1.1.0_schedule.md: -------------------------------------------------------------------------------- 1 | category: Изпит 2 | pinned: true 3 | tags: 4 | - elixir 5 | - schedule 6 | 7 | -------- 8 | # Разписание за поправителния изпит 9 | 10 | Ето го и разписанието за утре: 11 | 12 | **Който не се вижда може да дойде в 13:15** 13 | 14 | |10:00|10:45|11:30| 15 | |:-:|:-:|:-:| 16 | |Полина Кръстева|Женя Георгиева|Хрисина Тодоров| 17 | |Мюмюн Исмаил|Радослав Георгиев|Димитър Узунов| 18 | |Емил Кирилов| Mr. Nobody |Еслин Каранасуф| 19 | {: .table .table-striped .table-bordered} 20 | -------------------------------------------------------------------------------- /old_news_and_organisation_posts/news_exam.md: -------------------------------------------------------------------------------- 1 | category: Новини 2 | tags: 3 | - organization 4 | - course 5 | - exam 6 | 7 | -------- 8 | 9 | # Дата и час за изпит 10 | 11 | Изпитът е на 8-ми Юли, Събота от 10 часа сутринта, 326-та зала. 12 | 13 | Ще имаме after-party, от 20 часа същия ден. Ние черпим, че избрахте нашия курс. Ще направим event във фейкбук. 14 | Ще е частно парти (само ние) в едно барче, наречено 'Ателието'. 15 | -------------------------------------------------------------------------------- /old_news_and_organisation_posts/news_moving.md: -------------------------------------------------------------------------------- 1 | category: Новини 2 | tags: 3 | - organization 4 | - course 5 | 6 | -------- 7 | 8 | # Курсът се мести 9 | 10 | Новата ни лаборатория ще бъде зала 200 на ФМИ, а занятията остават по същото време. 11 | -------------------------------------------------------------------------------- /old_news_and_organisation_posts/news_no_entry_test.md: -------------------------------------------------------------------------------- 1 | category: Новини 2 | tags: 3 | - test 4 | - organization 5 | - course 6 | 7 | -------- 8 | 9 | # Няма да има входен тест 10 | 11 | След мъдри и задъхани дебати в Slack, екипът на курса по `Elixir` реши че: 12 | * Входен тест не е нужен. 13 | * Имате нужда да отпразнувате това с по бира! 14 | -------------------------------------------------------------------------------- /old_news_and_organisation_posts/news_pre_exam.md: -------------------------------------------------------------------------------- 1 | category: Новини 2 | tags: 3 | - organization 4 | - course 5 | - exam 6 | 7 | -------- 8 | 9 | # Дата и час за предварителна защита 10 | 11 | Няколко човека поискаха по-ранна дата, всеки, който желае и вече е готов може да се яви на нея. 12 | Датата е *24.06.2017, Събота*, часът е *13:00*, стаята е *311*. 13 | 14 | Следващата дата е 8-ми юли. Запазете си и вечерта на 8-ми юли, ще имаме изненада. 15 | -------------------------------------------------------------------------------- /old_news_and_organisation_posts/project.md: -------------------------------------------------------------------------------- 1 | title_image_path: LMAP_August_research.jpg 2 | category: Програма 3 | pinned: true 4 | tags: 5 | - elixir 6 | 7 | -------- 8 | 9 | # Инструкции за финален проект 10 | 11 | Трябва да предложите тема за проект до 23:59 на 31.05.2017 г. Предложението за проект трябва да се състои от не повече от 10 изречения, описващи функционалността на проекта ви, изпратени до **course@elixir-lang.bg**. В рамките на ден ще ви отговорим дали одобряваме проекта и колко точки максимум би ви донесъл той (40, 50, 60, 70 или 80). 12 | 13 | За да минете курса трябва да изкарате поне 20 точки от проект. 14 | 15 | Винаги 10 точки от проекта ви ще бъдат давани за добър функционален стил на писане на кода, 30% от максимума ще са за тестове и документация и останалите точки са за пълнота и функционалност на проекта ви. 16 | 17 | 18 | Ето как изглежда разбиването на точките за проектите от различните категории. 19 | 20 | |максимални точки|стил|тестове и документация|функционалност| 21 | |:--:|:--:|:--:|:--:| 22 | |40|10|12|18| 23 | |50|10|15|25| 24 | |60|10|18|32| 25 | |70|10|21|39| 26 | |80|10|24|46| 27 | {: .table .table-striped .table-bordered .table-hover} 28 | 29 | ## Класификация на проектите 30 | 31 | ### Проект за 40 точки 32 | 33 | Това е проект, който реализира секвенциална програма, изпълняваща се в един процес. 34 | 35 | ### Проект за 50 точки 36 | 37 | Проект за 50 точки трябва да има поне минимална паралелизация. 38 | 39 | ### Проект за 60 точки 40 | 41 | Трябва да направите **OTP application** със супервайзър дърво. 42 | 43 | ### Проект за 70 точки 44 | 45 | Трябва да имате **OTP application**, който да предоставя или използва някакъв ресурс онлайн. 46 | 47 | ### Проект за 80 точки 48 | 49 | Много рядко бихме дали 80 точки. Това са изключително функционални проекти, например: 50 | - някоя библиотека, която да е полезна за много хора 51 | - проект, в който има солидно (и интелигентно) мета програмиране 52 | - нещо дистрибутирано, за което има смисъл да бъде направено така 53 | 54 | ## Представяне на проекти 55 | 56 | Ще се състои на 8 Юли, като по-късно ще обявим график за представяне. Ако някой от вас смята, че тази дата е твърде късна за него, нека го отбележи в имейла с предложението за проект (и ще насрочим предварителна защита). 57 | -------------------------------------------------------------------------------- /old_news_and_organisation_posts/project_one.md: -------------------------------------------------------------------------------- 1 | --- 2 | category: Проект 3 | author: ivan 4 | tags: 5 | - elixir 6 | - project 7 | --- 8 | 9 | # Elixir курсов проект 10 | 11 | Disclaimer: За да пропуснете празните приказки и да видите самото условие на проекта вижте **Условие** 12 | 13 | ### Какъв проект и защо сега? 14 | За който не е разбрал - освен домашни и финален проект, то вие трябва да напишете и един малък проект в средата на семестъра. За разлика от домашните, тук няма да ви дадем списък с функции, които да имплементирате, тестове, с които да проверите коректността и т.н. Мисията да направите дизайна, да решите как ще изглежда публичния интерфейс на програмата и **да напишете тестове** е изцяло ваша. 15 | 16 | В днешно време голяма част от информацията в web пространството се предава чрез JSON. Но също така JSON е текстов формат - няма как с него да постигнем оптимална скорост и размер. Затова на сцената се появява MsgPack - двоичен формат за сериализация, за който от [сайта](https://msgpack.org) прочитаме: 17 | >It's like JSON. but fast and small. 18 | 19 | Може би сте ни чували на лекции да казваме, че Erlang/Elixir са подходящи езици за обработка на binary data, а и за имплементация на комуникационни протоколи. Фактът, че Erlang е създаден с цел писане на телеком програми си казва думата. Сега e ваш ред да опитате да направите точно това. 20 | 21 | ### Условие 22 | 23 | Вашата задача е да създадете mix проект, който предоставя функционалност за конвертиране JSON към MsgPack и MsgPack към JSON. Какви публични функции ще имате, какви аргументи ще приемат те, как ще е структуриран проектът - това са част от нещата, които трябва да решите сами. Също ваша важна задача е да напишете смислени и изчерпателни тестове. 24 | 25 | Наша задача, като преподаватели в курса на Elixir, е да ви даваме насоки и да отговаряме на въпроси. 26 | 27 | ##### Какво е JSON? 28 | - Обяснено простичко - [линк](https://stackoverflow.com/a/383699) 29 | - RFC - [линк](https://tools.ietf.org/html/rfc7159) 30 | 31 | ##### Какво е MsgPack? 32 | - Wikipedia - [линк](https://en.wikipedia.org/wiki/MessagePack) 33 | - Спецификация - [линк](https://github.com/msgpack/msgpack/blob/master/spec.md) 34 | 35 | MsgPack е формат за сериализация на данни в двоичен формат. 36 | 37 | MsgPack е JSON-compatible. ...A? 38 | Това значи, че MsgPack дефинира точно тези типове, които дефинира и JSON, и може без загуба на информация да се конвертира между JSON и MsgPack ([за разлика](https://stackoverflow.com/a/6357042) от BSON). Как би изглеждало това във вашия код? 39 | ```elixir 40 | msgpack_decoded_to_some_elixir_struct = decode_from_msg_pack(message) 41 | json = encode_to_json(msgpack_decoded_to_some_elixir_struct) 42 | ``` 43 | Тоест не е нужно да правите допълнителни стъпки върху данните, за да ги подготвите. 44 | 45 | ##### Пример 46 | Следният JSON с размер 27 байта 47 | > {"compact":true,"schema":0} 48 | 49 | конвертиран до MsgPack заема 18 байта (33% по-малко)и изглежда така: 50 | > 82 a7 63 6f 6d 70 61 63 74 c3 a6 73 63 68 65 6d 61 00 51 | 52 | За справки може да използвате [msgpack.org](https://msgpack.org/) и по-специално `Try!` секцията. 53 | 54 | ##### Неща, за които не изискваме да имплементирате поддръжка: 55 | - unsigned integer 56 | - float32 57 | - fixed ext 58 | - timestamp 59 | 60 | ##### Неща, които ще оценяваме: 61 | - Функционалност - Проектът трябва да имплементира коректно поне част от условието. 62 | - Тестове - "Тествах го като копирах един джейсън в конзолата и видях, че работи" не се приема. Искаме от вас да напишете смислени тестове. Опитайте се да покриете различни крайни случаи - празни аргументи, грешен формат и т.н. 63 | - Стил - Това е субективната част в оценяването на проекта. Малка част от нещата, които включва са: 64 | - (не)използване на `if`/`cond`. Забележете, че това **не** значи, че трябва да направите `case` с `true ->` и `false ->`. Това означава, че може би трябва да подходите по различен начин към проблема. 65 | - Дизайн и архитектура на проекта: 66 | - *разумно* разделение на файловете в поддиректории (ако е нужно) 67 | - *разумно* разделение на логиката в модулите 68 | - *разумно* използване на структури 69 | - абе, все субективни неща... 70 | - Функционално, а не Х програмиране `when X in ["ООП", "императивно", ...]`. Elixir, а не X транслиран до Elixir `when X in ["Java","Ruby", "C++", "C#", ...]` 71 | - Имена на функции/променливи - `encode`/`pack` са добри имена за функция, `fun` не е. 72 | - Форматиране на кода - пускайте редовно `mix format` 73 | - Наличие на документация и тип-спецификации. Не е нужно и да прекалявате с документацията. 74 | - и други. 75 | 76 | Преди датата за защита на проекта ще бъде публикувана скалата за оценка и колко точки ще носи всеки компонент. 77 | 78 | ### Краен срок 79 | Крайният срок за проекта е 8 май 2018г. Не е задължително (но е препоръчително) да държите кода си в github - ние ще го гледаме на защитите, а не предварително. 80 | 81 | PS 0: Бинарен протокол не е синоним на бързодействие - тези неща трябва да се измерят. Тази задача **не** влиза в условието на проекта, но ако ви остане време това е една чудесна възможност да видите как се случва benchmarking-ът в Elixir 82 | 83 | PS 1: Има много библиотеки, които имплементират условието. Не правете copy-paste на големи парчета код от тях. Може да не ви хванем, а може и да ви хванем. 84 | 85 | PS2: Някой каза ли binary pattern matching? 86 | 87 | 88 | -------------------------------------------------------------------------------- /old_news_and_organisation_posts/project_one_presentation.md: -------------------------------------------------------------------------------- 1 | --- 2 | category: Ресурси 3 | tags: 4 | - elixir 5 | - resources 6 | - facebook 7 | - twitter 8 | - email 9 | - github 10 | --- 11 | 12 | # Защита на семестриaлен проект 13 | 14 | Дойде време за защита на [семестриалните проекти](https://elixir-lang.bg/posts/project_one). 15 | 16 | Тя ще се проведе в обичайното време на лекцията **19:00 - 21:00** на **8-ми Май**. За да оптимизирам процеса моля да изпратите потвърждение, че ще участвате в зашитата на проектите, като изпратите имейл до **mail-bot@elixir-lang.bg** с относно (Subject) **Confirm**. 17 | 18 | В деня на защитата, по обяд, ще публикуваме статия с разпределение по часове. 19 | Ако не сте потвърдили, не се притеснявайте, може да дойдете на защитата, но може да се наложи да изчакате известно време. 20 | -------------------------------------------------------------------------------- /old_news_and_organisation_posts/register.md: -------------------------------------------------------------------------------- 1 | --- 2 | category: Ресурси 3 | pinned: true 4 | tags: 5 | - elixir 6 | - resources 7 | - facebook 8 | - twitter 9 | - email 10 | - github 11 | --- 12 | 13 | # Регистрация 14 | 15 | Оправихме мейл бота. Йей! 16 | 17 | Затова следващата ви задача е да се регистрирате през него. 18 | 19 | ## GitHub акаунт 20 | Ако нямате [GitHub](http://github.com) акаунт, време е да си направите такъв, ще ви трябва за регистрацията. 21 | 22 | ## За регистрация 23 | 24 | Трябва да изпратите имейл до **mail-bot@elixir-lang.bg** 25 | * В Относно (Subject) трябва да напишете **register**. 26 | * Съдържанието на имейла трябва да еследното: 27 | ``` 28 | name: #{Вашето име} 29 | fn: #{Вашия факултетен номер} 30 | github: #{Вашия гитхъб хендъл} 31 | ``` 32 | 33 | Например ако аз трябва да се регистрирам бих изпратил такъв имейл: 34 | ``` 35 | name: Слави Боянов 36 | fn: 80905 37 | github: Reductions 38 | ``` 39 | 40 | ## След това 41 | 42 | Има 3 опции: 43 | 44 | 1. До минута получавате имейл, който ви поздравява за регистрацията. 45 | 2. До минута получавате имейл, който ви казва, че е възникнала грешга при регистрацията. 46 | - най-вероятно сте сбъркали нещо 47 | - опитайте се да го оправите и да изпратите нов имейл за регистрация 48 | 3. Не получавате отговор до минута. 49 | - нещо се е счупило 50 | - най-вероятно аз съм виновен 51 | - препратете имейла на **course@elixir-lang.bg** 52 | 53 | ## След успешна регистрация 54 | 55 | Ще започнете да получавате имейли за домашни, проекти и по-важните новини за курса. 56 | -------------------------------------------------------------------------------- /old_news_and_organisation_posts/resources.md: -------------------------------------------------------------------------------- 1 | --- 2 | category: Ресурси 3 | tags: 4 | - elixir 5 | - resources 6 | - facebook 7 | - twitter 8 | - email 9 | - github 10 | --- 11 | 12 | # Къде можете да ни намерите 13 | 14 | На първата ни среща показахме различни места (виртуални), на които можете да ни намерите. 15 | 16 | Най-лесното за намиране място е тук, на този сайт/блог, затова и слагаме другите връзки тук. 17 | 18 | ## Връзки 19 | 20 | * Facebook група : https://www.facebook.com/groups/636900123169076 21 | * Github организация : https://github.com/ElixirCourse 22 | * Email на курса : course@elixir-lang.bg 23 | -------------------------------------------------------------------------------- /old_news_and_organisation_posts/schedule_eight_of_may.md: -------------------------------------------------------------------------------- 1 | --- 2 | category: Ресурси 3 | tags: 4 | - elixir 5 | - resources 6 | - facebook 7 | - twitter 8 | - email 9 | - github 10 | --- 11 | 12 | # Разписание за защита 13 | 14 | Днес ще е защитата на [семестриалните проекти](https://elixir-lang.bg/posts/project_one). 15 | Ето го разписанието: 16 | 17 | **Който не се вижда може да дойде в 20:30** 18 | 19 | |Час|Иван|Meddle|Слави| 20 | |:-:|:-:|:-:|:-:| 21 | |19:15|81174|855271|80945| 22 | |19:40|61808|45203|45149| 23 | |20:05|45135|80797|71638| 24 | |20:30|31319|45194| | 25 | {: .table .table-striped .table-bordered} 26 | -------------------------------------------------------------------------------- /old_news_and_organisation_posts/schedule_exam.md: -------------------------------------------------------------------------------- 1 | --- 2 | category: Ресурси 3 | tags: 4 | - elixir 5 | - resources 6 | - facebook 7 | - twitter 8 | - email 9 | - github 10 | --- 11 | 12 | # Разписание за изпит 13 | 14 | Утре, (**07.07**), в зала 107 ще е защитата на [проектите](https://elixir-lang.bg/posts/project). 15 | Ето го разписанието: 16 | 17 | **Който не се вижда може да дойде в 11:30** 18 | 19 | |Час|Вальо|Ники|Слави|Meddle|Иван| 20 | |:-:|:-:|:-:|:-:|:-:|:-:| 21 | |10:00|81174 |81138|61921|61808|45135| 22 | |10:45|80797 |45059|45203|61840|174 | 23 | |11:30|855271| | | | | 24 | {: .table .table-striped .table-bordered} 25 | -------------------------------------------------------------------------------- /old_news_and_organisation_posts/summary.md: -------------------------------------------------------------------------------- 1 | category: Summary 2 | pinned: true 3 | tags: 4 | - elixir 5 | 6 | -------- 7 | 8 | # Обобщение на курса 9 | 10 | Тук можете да намерите обща информация за курса. 11 | 12 | ## Ресурси 13 | 14 | Линк към блога ни: 15 | 16 | Линк към Facebook групата ни: 17 | 18 | Линк към GitHub организацията ни: 19 | 20 | Можете да задавате въпроси на имейла: [course@elixir-lang.bg](mailto:course@elixir-lang.bg) 21 | 22 | ## Домашни 23 | 24 | За да се регистрирате за получаване на домашни изпратете имейл до адреса [register@elixir-lang.bg](mailto:register@elixir-lang.bg) със **Subject:** *register* и **Съдържание**: *име и фамилия, факултетен номер (ако имате такъв) и линк към GitHub акаунта ви*. 25 | 26 | ### Задача 1 27 | 28 | - [Условие](https://blog.elixir-lang.bg/posts/task_one) 29 | 30 | - Краен срок 23:59 06.03.2017 г. 31 | 32 | - Точки 4 33 | 34 | - [Примерно решение](https://github.com/ElixirCourse/task-one/blob/master/task.exs) 35 | 36 | ### Задача 2 37 | 38 | - [Условие](https://blog.elixir-lang.bg/posts/task_two) 39 | 40 | - Краен срок 23:59 03.04.2017 г. 41 | 42 | - Точки 9 (+1) 43 | 44 | - [Примерно решение](https://github.com/ElixirCourse/task-two/) 45 | 46 | ### Задача 3 47 | 48 | - [Условие](https://blog.elixir-lang.bg/posts/task_three) 49 | 50 | - Краен срок 20:00 10.05.2017 г. 51 | 52 | - Точки 9 53 | 54 | ## Теми 55 | 56 | ### 1. Защо Elixir? 57 | 58 | - [блог](https://blog.elixir-lang.bg/posts/why_elixir) 59 | 60 | - [презентация](https://gitpitch.com/ElixirCourse/why-elixir-presentation) 61 | 62 | ### 2. Типове и съпоставяне на образци 63 | 64 | - [блог](https://blog.elixir-lang.bg/posts/pattern_matching_types_and_basics) 65 | 66 | - [презентация](https://gitpitch.com/ElixirCourse/types-and-pattern-matching-presentation) 67 | 68 | - допълнителни материали 69 | 70 | - [рекурсия с анонимни функции (или как да си направим Y-комбинатор)](https://blog.elixir-lang.bg/posts/y) 71 | 72 | 73 | ### 3. Модули, функции и рекурсия 74 | 75 | - [блог](https://elixir-lang.bg/posts/modules_functions_recursion) 76 | 77 | - [презентация](https://gitpitch.com/ElixirCourse/modules-functions-recursion-presentation) 78 | 79 | 80 | ### 4. Създаване и подържане на проекти с Mix 81 | 82 | ### 5.1. Низове и binaries 83 | 84 | - [блог за binaries](https://elixir-lang.bg/posts/binaries) 85 | 86 | - [блог за низов](https://elixir-lang.bg/posts/strings) 87 | 88 | - [презентация](https://gitpitch.com/ElixirCourse/binaries_and_strings_presentation) 89 | 90 | ### 5.2. Речници, структури и протоколи 91 | 92 | - [блог за речници и стуктури](https://elixir-lang.bg/posts/maps_structs) 93 | 94 | - [блог за протоколи](https://elixir-lang.bg/posts/protocols) 95 | 96 | - [презентация](https://gitpitch.com/ElixirCourse/maps_structs_protocols_presentation) 97 | 98 | ### 6. Списъци, потоци и рекурсия 99 | 100 | - [блог](https://elixir-lang.bg/posts/lists_streams_recursion) 101 | 102 | - [презентация](https://gitpitch.com/ElixirCourse/list-streams-presentation) 103 | 104 | ### 7.1. Контролни структури 105 | 106 | - [блог](https://elixir-lang.bg/posts/control_flow) 107 | 108 | ### 7.2. Грешки 109 | 110 | - [блог](https://elixir-lang.bg/posts/exceptions) 111 | 112 | ### 7.3. Вход и изход 113 | 114 | - [блог](https://elixir-lang.bg/posts/input_output) 115 | 116 | ### 8. Процеси 117 | 118 | - [блог](https://elixir-lang.bg/posts/processes) 119 | 120 | ### 9. Устройство и комуникация между процеси 121 | 122 | - [блог](https://elixir-lang.bg/posts/process_internals) 123 | 124 | ## Оценяване 125 | 126 | - Максимум 100 точки: 127 | - Неизвестен брой домашни за общо 40 точки. 128 | - Kурсов проект за 40 до 80 точки, oпределени от нас спрямо сложносттан на проекта (Най-често 60 точки). 129 |
130 | - Допълнителни точки: 131 | - Бонус точки, ако добавите нещо, към някой проект с отворен код свързан с Elixir. 132 | - Бонус точки за неща, които ние преценим. 133 |
134 | - Скала за оценяване 135 | 136 | 137 | | Оценнка | 2 | 3 | 4 | 5 | 6 | 138 | | :--: | :--: | :--: | :--: | :--: | :--: | 139 | | Точки | 0 - 49 | 50 - 57 | 58 - 74 | 75 - 91 | 92 -∞ | 140 | {: .table .table-striped .table-bordered .table-hover} 141 | -------------------------------------------------------------------------------- /old_news_and_organisation_posts/summer_exam_2018.md: -------------------------------------------------------------------------------- 1 | --- 2 | category: Проект 3 | author: ivan 4 | tags: 5 | - elixir 6 | - exam 7 | --- 8 | # Поправителна сесия 9 | 10 | Изпитът по Elixir през поправителната сесия ще се състои на **25.08.2018г.** от 13:00 часа в зала 305. 11 | 12 | За него важат същите правила както и за оценяването през редовната сесия. За тях можете да прочетете подробно [тук](https://elixir-lang.bg/posts/project). 13 | 14 | Ако имате предложен и приет проект от редовната сесия, то продължавате с него. Ако нямате - изпратете ни мейл в **най-кратки срокове** (краен срок **10.08.2018г.**) като следвате инструкциите [тук](https://elixir-lang.bg/posts/project). 15 | 16 | Припомняме за правилото, че за да завършите курса трябва: 17 | - Да имате >= 50 точки 18 | - Да имате >= 20 точки на **поне един** от двата проекта 19 | 20 | ### Промяна на оценките от домашни и малък проект 21 | 22 | Имате възможност да се откажете от **всичките** си точки от домашни и малък проект (15т + 15т + 30т) изкарани през семестъра и да напишете нови три задачи. 23 | Новите три задачи са: 24 | - 25 | - 26 | - 27 | 28 | Третата задача съответства на малкия проект. 29 | 30 | Как ще се случи това? Не е нужно предварително да заявите, че искате да си промените точките. По време на защитата може да се откажете от старите си точки, да оценим написаните от Вас задачи и да получите нови. 31 | 32 | # Важно 33 | С написването на новите задачи Вие **променяте** Вашите точки. Това означава, че ако изкарате по-малко точки, то Вие ще **понижите** Вашите точки. 34 | -------------------------------------------------------------------------------- /old_news_and_organisation_posts/task_four.md: -------------------------------------------------------------------------------- 1 | category: Домашно 2 | 3 | tags: 4 | - elixir 5 | - homework 6 | 7 | -------- 8 | 9 | # Задача 4 10 | 11 | Четвъртата задача е за **9 точки**. 12 | 13 | Крайният срок за предаване е 23:59 на 29.05.2017 г. 14 | 15 | ## Информация за задачата 16 | Ако все още не сте се записали за получаване на домашни, можете да го направите, като изпратите имейл до адрес **register@elixir-lang.bg**, който трябва да отговаря на следните условия: 17 | * В Относно (Subject) трябва да напишете **register**. 18 | * В съдържанието на мейла трябва да напишете: име и фамилия, факултетен номер и да сложите линк към GitHub акаунта ви. 19 | 20 | Ако сте записани, сте получили мейл с линк към заданието за новото домашно. То съдържа основната структура на проекта, в който трябва да работите. Свободни сте да създавате колкото искате допълнителни файлове, стига те да се намират в една от директориите `lib` или `test` (не пишете във вече съществуващите файлове в `test`). В репото ще има няколко базови теста, които да ви съпътстват при писането на задачата (Пускайте тестовете често, за да следите прогреса си). Често "къмитвайте" промените във вашето локално хранилише, също така често "пушвайте" към съответното в GitHub. Ще се постараем да върнем обратна връзка в рамките на един ден (ако не получите такава, може да пишете мейл на **course@elixir-lang.bg**, за да ни напомните). Всички работи, предадени в последния ден от срока за предаване, няма да получат обратна връзка, преди да бъдат окончателно проверени. 21 | 22 | ## Lambda 23 | 24 | Задачата е проста (на пръв поглед). Трябва да направите **OTP application** `Lambda.Application`, която да стартира **Supervisor** `Lambda.Supervisor` с поне едно дете **GenServer** регистриран с име `Lambda.Server`. Сървърът трябва да регистрира изпълнението на функция с дадени аргументи и да кешира резултата от нея. При всяко следващо извикване резултатът не трябва да бъде пресмятан отново, ами да се връща кеширания резултат от сървъра. 25 | 26 | За реализацията на функционалността можете да добавите още неща в **Супервижън дървото** на вашата апликация. 27 | 28 | Прочетете цялата задача преди да почнете да пишете по нея, включително забележката в края на документа. 29 | 30 | ### Интерфейс на клиента 31 | 32 | Част от следващите функции трябва да са синхронни, а останалите несинхронни. Ще ги бележа съответно с **[call]** и **[cast]**. 33 | 34 | #### Lambda.Server.register(module, function) [cast] 35 | 36 | Тази функция казва на сървъра, че от тук нататък може да очаква извикването на функцията `function` от модула `module`. 37 | 38 | Тук точно като при `spawn/3` `module` и `function` са атоми. 39 | Трябва да връща `:ok` при успешна регистрация. 40 | 41 | #### Lambda.Server.run(module, function, arguments) [call] 42 | 43 | Изпълнява функцията `function` от модула `module` с аргументи `arguments` (списък от аргументите на функцията) 44 | - ако функцията не е регистрирана преди това, отговора на сървъра трябва да е `{:error, "Function is not registered."}` 45 | - ако функцията се изпълнява за първи път с тези аргументи тя се изпълнява и се връща резултата във наредена двойка `{:ok, result}` 46 | - ако функцията се извиква повторно с тези аргументи, то се връща кешираната стойност отново във вида `{:ok, result}` 47 | 48 | За да можем да разгледаме няколко примера нека си представим, че имаме следния модул: 49 | 50 | ```elixir 51 | defmodule Test do 52 | def wait(time) when time > 0 and is_integer(time) do 53 | Process.wait(time) 54 | time 55 | end 56 | 57 | def crasher() do 58 | raise "I will crash you!" 59 | end 60 | end 61 | ``` 62 | 63 | Тогава: 64 | 65 | ```elixir 66 | iex> Lambda.Server.run(Test, :wait, [1]) 67 | {:error, "Function is not registered"} 68 | iex> Lambda.Server.register(Test, :wait) 69 | :ok 70 | iex> Lambda.Server.run(Test, :wait, [1]) 71 | {:ok, 1} 72 | iex> :timer.tc fn -> Lambda.Server.run(Test, :wait, [1000]) end 73 | {1024969, {:ok, 1000}} 74 | iex(4)> :timer.tc fn -> Lambda.Server.run(Test, :wait, [1000]) end 75 | {36, {:ok, 1000}} 76 | ``` 77 | 78 | Както можете да видите от примера, вторият път, когато извикаме `Test.wait` с аргумента `1000`, функцията изобщо не се изпълнява, а просто се връща резултата от предходното изпълнение. Затова извикването завършва много по-бързо втория път (изчакването `Process.wait(1000)` не се случва). 79 | 80 | Ако при извикване на `run` възникне грешка от каквото и да е естество, резултатът от функцията трябва да е `{:error, "Execution error"}`. 81 | 82 | ```elixir 83 | iex> Lambda.Server.register(Test, :crasher) 84 | :ok 85 | iex> Lambda.Server.run(Test, :crash, []) 86 | {:error, "Execution error"} 87 | ``` 88 | 89 | #### Lambda.Server.cached(module, function, arguments) [call] 90 | 91 | Проверява дали има кеширан резултат от изпълнението на функцията `function` от модула `module` с аргументи `arguments`: 92 | - ако функцията не е регистрирана преди това, отговорът на сървъра трябва да е `{:error, "Function is not registered."}` 93 | - ако резултатът от функцията не е кеширан, за тези аргументи се връща `{:error, "Not cached"}` 94 | - ако резултатът е кеширан се връщa `{:ok, result}` 95 | 96 | 97 | ```elixir 98 | iex> Lambda.Server.cached(Test, :wait, [1]) 99 | {:error, "Function is not registered"} 100 | iex> Lambda.Server.register(Test, :wait) 101 | :ok 102 | iex> Lambda.Server.cached(Test, :wait, [1]) 103 | {:error, "Not cached"} 104 | iex> Lambda.Server.run(Test, :wait, [1]) 105 | {:ok, 1} 106 | iex> Lambda.Server.cached(Test, :wait, [1]) 107 | {:ok, 1} 108 | ``` 109 | 110 | #### Lambda.Server.clear_cache(module, function) [cast] 111 | 112 | Функцията e асинхронна (не чака резултат от сървъра). Трябва да изчиства цялата кеширана информация за функцията `function` от модула `module`. Ако тя е била регистрирана преди това, то тя трябва да остане такава и след това. Ако не е била регистрирана не се случва нищо. 113 | 114 | 115 | ```elixir 116 | iex> Lambda.Server.register(Test, :wait) 117 | :ok 118 | iex> Lambda.Server.run(Test, :wait, [1]) 119 | {:ok, 1} 120 | iex> Lambda.Server.cached(Test, :wait, [1]) 121 | {:ok, 1} 122 | iex> Lambda.Server.clear_cache(Test, :wait) 123 | :ok 124 | iex> Lambda.Server.cached(Test, :wait, [1]) 125 | {:error, "Not cached"} 126 | ``` 127 | 128 | #### Lambda.Server.clear_cache(modlue, function, arguments) [cast] 129 | 130 | Функцията e асинхронна (не чака резултат от сървъра). Трябва да изчиства кешираната информация за функцията `function` от модула `module` при аргументи `arguments`. 131 | 132 | ```elixir 133 | iex> Lambda.Server.register(Test, :wait) 134 | :ok 135 | iex> Lambda.Server.run(Test, :wait, [1]) 136 | {:ok, 1} 137 | iex> Lambda.Server.run(Test, :wait, [2]) 138 | {:ok, 2} 139 | iex> Lambda.Server.clear_cache(Test, :wait, [1]) 140 | :ok 141 | iex> Lambda.Server.cached(Test, :wait, [1]) 142 | {:error, "Not cached"} 143 | iex> Lambda.Server.cached(Test, :wait, [2]) 144 | {:ok, 2} 145 | ``` 146 | 147 | #### Lambda.Server.unregister(module, function) [cast] 148 | 149 | Тази функция казва на сървъра, че не се очаква извикване на функцията `function` от модула `module` и че не е необходимо да се съхранява повече информацията за функцията. 150 | 151 | ```elixir 152 | iex> Lambda.Server.register(Test, :wait) 153 | :ok 154 | iex> Lambda.Server.run(Test, :wait, [1]) 155 | {:ok, 1} 156 | iex> Lambda.Server.unregister(Test, :wait) 157 | :ok 158 | iex> Lambda.Server.run(Test, :wait, [1]) 159 | {:error, "Function is not registered"} 160 | iex> Lambda.Server.register(Test, :wait) 161 | :ok 162 | iex> Lambda.Server.cached(Test, :wait, [1]) 163 | {:error, "Not cached"} 164 | ``` 165 | 166 | ### Забележка 167 | 168 | За получаване на пълния брой точки **НЕ Е** необходимо сървърът да изпълнява повече от една заявка на наведнъж. Показаното по-долу е съвсем приемливо поведение: 169 | 170 | ```elixir 171 | iex> Lambda.Server.register(Test, :wait) 172 | :ok 173 | iex> task = 174 | ...> fn(pid, seconds) -> 175 | ...> result = Lambda.Server.run(Test, :wait, [seconds*1000]) 176 | ...> send(pid, result) 177 | ...> end 178 | #Function<...> 179 | iex> one_second_task = fn -> task(self(), 1) end 180 | #Function<...> 181 | iex> ten_second_task = fn -> task(self(), 10) end 182 | #Function<...> 183 | iex> Process.spawn(ten_second_task, []) 184 | #PID<0.123.0> 185 | iex> Process.spawn(one_second_task, []) 186 | #PID<0.126.0> 187 | iex> Process.sleep(8000) 188 | :ok 189 | iex> flush() # нито една от заявките не е върнала резултат 190 | :ok # защото втората чака първата да приключи 191 | iex> Process.sleep(8000) 192 | :ok 193 | iex> flush() 194 | {:ok, 10000} # вече и двете заявки са приключили 195 | {:ok, 1000} # двата отговора идват в реда в който са изпратени 196 | :ok 197 | ``` 198 | 199 | Ако сървърът ви има възможност да обработва повече от една заявка ще получите бонус точки (**4.5т**). Тогава горният сегмент би изглеждал така: 200 | 201 | 202 | ```elixir 203 | iex> Lambda.Server.register(Test, :wait) 204 | :ok 205 | iex> task = 206 | ...> fn(pid, seconds) -> 207 | ...> result = Lambda.Server.run(Test, :wait, [seconds*1000]) 208 | ...> send(pid, result) 209 | ...> end 210 | #Function<...> 211 | iex> one_second_task = fn -> task(self(), 1) end 212 | #Function<...> 213 | iex> ten_second_task = fn -> task(self(), 10) end 214 | #Function<...> 215 | iex> Process.spawn(ten_second_task, []) 216 | #PID<0.123.0> 217 | iex> Process.spawn(one_second_task, []) 218 | #PID<0.126.0> 219 | iex> Process.sleep(8000) 220 | :ok 221 | iex> flush() 222 | {:ok, 1000} # По-бързата заявка е приключила първа и връща отговор 223 | :ok 224 | iex> Process.sleep(8000) 225 | :ok 226 | iex> flush() 227 | {:ok, 10000} # По-бавната заявка завършва също и връща отговор 228 | :ok 229 | ``` 230 | -------------------------------------------------------------------------------- /old_news_and_organisation_posts/task_one.md: -------------------------------------------------------------------------------- 1 | category: Домашно 2 | tags: 3 | - elixir 4 | - homework 5 | 6 | -------- 7 | 8 | # Задача 1 9 | 10 | Първата задача е доста лесна и може да ви донесе **4 точки**. 11 | 12 | Крайния срок за предаване е 23:59ч на 06.03.2017 г. 13 | 14 | ## GitHub акаунт 15 | Ако нямате [GitHub](http://github.com) акаунт, време е да си направите такъв, ще ви трябва за домашното. 16 | 17 | ## Изпратете ни мейл 18 | 19 | Имейлът трябва да бъде изпратен до **register@elixir-lang.bg** и трябва да отговаря на следните условия: 20 | * В Относно (Subject) трябва да напишете **register**. 21 | * В съдържанието на мейла трябва да напишете: име и фамилия, факултетен номер и да сложите линк към GitHub акаунта ви 22 | 23 | Ще ви бъде върнат автоматичен отговор с линк. За да отворите този линк ще трябва да влезете в GitHub акаунта си. Когато го направите за вас ще бъде създадено изолирано хранилище, където трябва да работите. 24 | 25 | ## Работа по задачата 26 | Към хранилището ви трябва да направите поне два отделни **commit**-а, всеки от тях трябва да добавя към хранилището един от следните файлове: 27 | * answers.md 28 | * task.exs 29 | 30 | ### Въпроси и отговори 31 | Във файла **answers.md**, трябва да има следните въпроси придружени с отговор: 32 | * Коя специалност и кой курс сте? (ако не учите във ФМИ го оставете без отговор) 33 | * Защо записахте (посещавате) курса? 34 | * Колко уверен се чувствате в използването на **_git_**? (число в интервала [0;10]) 35 | * Колко често проверявате мейла си? (едно от: ***никога***, ***веднъж месечно***, ***веднъж седмично***, ***всеки ден***) 36 | 37 | Примерен отговор би изглеждал така: 38 | 39 | > #### Коя специалност и кой курс сте? 40 | > Компютърни науки, пети курс. 41 | > 42 | > #### Защо записахте (посещавате) курса? 43 | > Една от основните причини да посещавам курса е, че участвам 44 | > в организацията му. По-значимата причина е защото смятам, че 45 | > Elixir е един от най-интерсените езици, с които съм се 46 | > сблъсквал. Това че е функционален, конкурентният му модел 47 | > и нестандартните конструкции в езика, засилват интереса ми 48 | > към Elixir още повече. 49 | > 50 | > #### Колко увере се чувствате в използването на **_git_**? 51 | > 6.5/10 52 | > 53 | > #### Колко често проверявате мейла си? 54 | > Всеки ден (толкова често, че от gmail понякога ме блокират) 55 | 56 | ### Малко код като за финал 57 | 58 | Във файла **task.exs** трябва да има 2 анонимни функции, които са свързани със следните имена: 59 | * if_else - симулираща действието на if клауза в императивните езици. Тя трябва да приема 3 аргумента. Първият аргумент е произволна стойност - ако тя се интерпретира като истина, то резултатът от функцията трябва да е вторият ѝ аргумент. Aко обаче първият аргумент се интерпретира като лъжа, то резултатът трябва да е третият аргумент на функцията. Например: 60 | 61 | ``` elixir 62 | iex> if_else.(true, "yes", "no") 63 | "yes" 64 | iex> if_else.(:false, "yes", "no") 65 | "no" 66 | ``` 67 | 68 | * if_else_lazy - подобно на предната функция приема 3 аргумента с разликата, че вторият и третият аргумент са функции на 0 аргумента, като съответната функция трябва да бъде изпълнена само ако нейният резултат трябва да бъде върнат. Например: 69 | 70 | ``` elixir 71 | iex> truth = fn() -> 72 | ...> IO.puts "You are right." 73 | ...> "yes" 74 | ...> end 75 | #Function<20.52032458/0 in :erl_eval.expr/5> 76 | iex> lie = fn() -> 77 | ...> IO.puts "You are lying to me!" 78 | ...> "no" 79 | ...> end 80 | #Function<20.52032458/0 in :erl_eval.expr/5> 81 | iex> if_else_lazy.(:true, truth, lie) 82 | You are right. 83 | "yes" 84 | iex> if_else_lazy.(false, truth, lie) 85 | You are lying to me! 86 | "no" 87 | ``` 88 | -------------------------------------------------------------------------------- /old_news_and_organisation_posts/task_one_2018.md: -------------------------------------------------------------------------------- 1 | --- 2 | category: Домашно 3 | author: njichev 4 | tags: 5 | - elixir 6 | - homework 7 | --- 8 | 9 | # Задача 1 10 | 11 | Първата задача е за общо **15 точки**. 12 | 13 | Крайният срок за предаване е 23:59 на 11.04.2018 г. 14 | 15 | Ако имате въпрос по домашното пишете на: course@elixir-lang.bg 16 | 17 | ## GitHub 18 | Ако нямате [GitHub](github.com) акаунт, време е да си направите такъв, ще ви трябва за домашното. 19 | 20 | След като кликнете на следващия линк и се логнете в GitHub, за вас ще бъде създадено репо, в което да качвате работата си. 21 | 22 | https://classroom.github.com/a/nqor38Gg 23 | 24 | Къмитвайте и пушвайте често. Ние ще се стараем да направим ревю на кода ви. 25 | 26 | ## Тестoве 27 | 28 | В репото, което ще получите, има няколко дефинирани теста. 29 | С тях можете да проверявате дали сте имплементирали най-базовата функционалност. 30 | 31 | ___ 32 | 33 | # Имплементирайте TestSuite 34 | 35 | Задачата е да напишете модул с име `TestSuite`, в който да дефинирате структура, която ще държи набор от тестове. 36 | 37 | Тестовете са анонимни функции, или именовани такива хванати с оператора &(capture-operator) с арност 0. 38 | Тест се смята за неуспешен, ако след изпълнението си - функцията върне `false`, `nil` или хвърли грешка. 39 | Ако функцията върне нещо резлично от `false` или `nil`, то съответният тест е успешен. 40 | 41 | Всъщност един тест може да има 5 различни състояния: 42 | 43 | 1. Преди да бъде пуснат - ще го бележим с `:pending` 44 | 2. Успешен - `:passed` 45 | 3. Провалил се - `:failed` 46 | 4. Пропуснат - `:skipped` 47 | 5. Не завършил за дадено време - `:timed_out` 48 | 49 | ## Функции, които TestSuite трябва да съдържа. 50 | 51 | #### TestSuite.new() 52 | 53 | Създава нов празен `TestSuite`, аналогично на `%TestSuite{}` 54 | 55 | ```elixir 56 | TestSuite.new() == %TestSuite{} 57 | # => true 58 | ``` 59 | #### TestSuite.new(initial_tests) 60 | Създава `TestSuite`, който съдържа всички тестове от `initial`. 61 | Ако тестовете са повече от един, то те се добавят в реда, в който ги виждаме. (Това ще е от значение по-нататък и важи също за следващата функция). 62 | 63 | ```elixir 64 | # Всеки тест е проста анонимна функция. 65 | f = fn -> 3 == 3 end 66 | g = fn -> 3 == 4 end 67 | 68 | TestSuite.new([f, g]) 69 | ``` 70 | 71 | #### TestSuite.add(test_suite, to_add): 72 | `=to_add` е една анонимна функция или списък от няколко такив. 73 | 74 | ```elixir 75 | f = fn -> 3 == 3 end 76 | g = fn -> 3 == 4 end 77 | 78 | TestSuite.new() |> TestSuite.add(f) 79 | TestSuite.new() |> TestSuite.add([f, g]) 80 | ``` 81 | 82 | #### TestSuite.add(test_suite, to_add, tags) 83 | 84 | Вторият параметър е същия, като при `TestSuite.add/2`. 85 | Третият съдържа списък от атоми или един атом, това са таговете на добавените тестове чрез тази функция. 86 | Таговете ще използваме по-късно за да филтрираме тестовете при изпълнението на други функции. 87 | 88 | ```elixir 89 | f = fn -> 3 == 3 end 90 | g = fn -> 3 == 4 end 91 | 92 | TestSuite.new() |> TestSuite.add(f, [:slow, :do_not_run]) 93 | TestSuite.new() |> TestSuite.add([f, g], :maybe) 94 | ``` 95 | 96 | #### TestSuite.size(test_suite) 97 | 98 | Връща размера на TestSuite-а (борая на тестовете в ного). 99 | 100 | ``` 101 | TestSuite.new() |> TestSuite.size() 102 | # => 0 103 | TestSuite.new([fn -> true end]) |> TestSuite.size() 104 | # => 1 105 | ``` 106 | 107 | #### TestSuite.size(test_suite, options) 108 | 109 | Връща борая на тестовете филтрирани чевз аргумента `options`. 110 | `options` e асоциативен списък, който може да съдържа следните "филтр": 111 | - `:only` - ако има такава опция се връща броя на тестовете с даден таг 112 | 113 | ```elixir 114 | TestSuite.new() 115 | |> TestSuite.add(fn -> true end, :always_true) 116 | |> TestSuite.add([fn -> false end, fn -> nil end], :never_true) 117 | |> TestSuite.size(only: :never_true) 118 | #=> 2 119 | ``` 120 | - `:exclude` - ако има такава опция се връща броя на тестовете, които не съдържат даден таг 121 | 122 | ```elixir 123 | TestSuite.new() 124 | |> TestSuite.add(fn -> true end, :always_true) 125 | |> TestSuite.add([fn -> false end, fn -> nil end], :never_true) 126 | |> TestSuite.size(exclude: :never_true) 127 | #=> 1 128 | ``` 129 | - можем да комбинираме двете например: 130 | 131 | ```elixir 132 | TestSuite.new() 133 | |> TestSuite.add(fn -> true end, :true) 134 | |> TestSuite.add(fn -> false end, :false) 135 | |> TestSuite.add(fn -> rem(:rand.uniform(), 2) == 0 end, [:true, :false]) 136 | |> TestSuite.size(only: :true, exclude: :flase) 137 | #=> 1 138 | ``` 139 | #### TestSuite.run(test_suite, options \\ []) 140 | 141 | Изпилнява тестовете и връща модифицирана `TestSuite` с информация за изпълнените тестове. 142 | Информация ще ни трябва по-нататък. 143 | 144 | ```elixir 145 | f = fn -> 3 == 3 end 146 | g = fn -> 3 == 4 end 147 | 148 | TestSuite.new() 149 | |> TestSuite.add(f) # Ще бъде маркиран, като :passed 150 | |> TestSuite.add(g) # Ще бъде маркиран, като :failed 151 | |> TestSuite.run() 152 | ``` 153 | 154 | Първите две опции са като при `TestSuite.size/2` (`:only`, `:exclude`), като всеки тест, който бъде филтриран не се изпълнява, а се маркира, катo `:skipped` 155 | 156 | ```elixir 157 | f = fn -> 3 == 3 end 158 | g = fn -> 3 == 4 end 159 | 160 | TestSuite.new() 161 | |> TestSuite.add(f, :first) # Ще се изпълни 162 | |> TestSuite.add(g, :second) # Ще бъде маркиран, като пропуснат 163 | |> TestSuite.run(only: :first) 164 | ``` 165 | 166 | Другата опция за изпълнението на тестовете е `:timeout` (тя има дефаултна стойност 5000 ms) 167 | 168 | Ако един тест работи повече от това време той бива маркиран, като `:timed_out` 169 | 170 | ```elixir 171 | f = fn -> 172 | Process.sleep(2000) 173 | true 174 | end 175 | 176 | TestSuite.new() 177 | |> TestSuite.add(f) # Ще бъде маркиран, като :timed_out 178 | |> TestSuite.run(timeout: 500) 179 | 180 | TestSuite.new() 181 | |> TestSuite.add(f) # Ще бъде маркиран, като :passed 182 | |> TestSuite.run(timeout: 5000) 183 | ``` 184 | Можем да run-нем `TestSuite`, който е върнат от `run`, в такъв случай ще изпълним само функциите, които не са маркирани, като `:passed` 185 | 186 | Всъщност се очаква да има фукции `timed_out`, `failed` и `skipped`, които правят същото като `passed`, но за съответните състояния. 187 | 188 | #### TestSuite.passed(test_suite) 189 | Връща нов `TestSuite` съдържащ само тезтовете, които са били изпълнени и са били успешни 190 | 191 | ```elixir 192 | f = fn -> 3 == 3 end 193 | g = fn -> 3 == 4 end 194 | 195 | passed = 196 | TestSuite.new() 197 | |> TestSuite.add(f) 198 | |> TestSuite.add(g) 199 | |> TestSuite.run() 200 | |> TestSuite.passed() 201 | |> TestSuite.size() 202 | 203 | TestSuite.size(passed) # passed съдържа само "теста" f, който е отбелязана като :passed 204 | #=> 1 205 | ``` 206 | 207 | #### TestSuite.failed(test_suite) 208 | #### TestSuite.timed_out(test_suite) 209 | #### TestSuite.skipped(test_suite) 210 | #### TestSuite.pending(test_suite) 211 | 212 | Последните 4 са аналогични на `TestSuite.passed/1`, но за съответните тест статуси. 213 | 214 | #### TestSuite.ran?(test_suite) 215 | 216 | Връща `true`, ако TestSuite-а е върнат от `TestSuite.run/2` 217 | 218 | ```elixir 219 | TestSuite.new() |> TestSuite.ran?() # => false 220 | 221 | TestSuite.new() |> TestSuite.run() |> TestSuite.ran?() # => true 222 | 223 | TestSuite.new() 224 | |> TestSuite.run() 225 | |> TestSuite.add(fn -> true end) 226 | |> TestSuite.ran?() # => false 227 | ``` 228 | #### TestSuite.reset(test_suite) 229 | 230 | Връща нов `TestSuite` подобен на входния, само че всички тестове са отбелязани, като `:pending`. 231 | 232 | ```elixir 233 | reset_suite = 234 | TestSuite.new(fn -> true end) 235 | |> TestSuite.run() 236 | |> TestSuite.reset() 237 | 238 | reset_suite 239 | |> TestSuite.size() 240 | #=> 1 241 | 242 | reset_suite 243 | |> TestSuite.pending() 244 | |> TestSuite.size() 245 | #=> 1 246 | ``` 247 | 248 | ### Протоколът `Inspect` 249 | 250 | Трябва да имплементирате протокола [`Inspect`](https://hexdocs.pm/elixir/Inspect.html#content) за структурата `TestSuite` 251 | 252 | Когато "инспектираме" един празен `TestSuite`, трябва да получим следния резултат: 253 | 254 | ```elixir 255 | t = TestSuite.new() 256 | 257 | inspect(t) #=> "#TestSuite<0 tests>" 258 | ``` 259 | 260 | Ако имаме тестове, които сме пускали, нещата са малко по различни: 261 | 262 | ```elixir 263 | f = fn -> 3 == 3 end 264 | g = fn -> 3 == 4 end 265 | h = fn -> Process.sleep(1000) end 266 | j = fn -> true end 267 | 268 | test_suite = 269 | TestSuite.new() 270 | |> TestSuite.add(f) 271 | |> TestSuite.add(g) 272 | |> TestSuite.add(h) 273 | |> TestSuite.add(j, :skip) 274 | |> TestSuite.run(exclude: :skip, timeout: 500) 275 | |> TestSuite.add(fn -> nil end) 276 | |> inspect() 277 | 278 | inspect(test_suite) 279 | #=> "#TestSuite<5 tests:.FTSP>" 280 | ``` 281 | 282 | Където всеки успешен тест е отбелязан с ".", всеки провали се - с "F", всеки пропуснат - със "S", всеки, който не е минал за допустимо време - "T" и всеки още не е изпълнен - с "P" 283 | 284 | Реда, в който показваме резултатите, е същият в който са били добавени в `TestSuite`-a. 285 | 286 | # Бонус точки 287 | 288 | Добавете опция на `:parallel` брой на тестове, които да се изпълняват конкурентно. 289 | Имплементирайте схема за паралелизиране на тестовете. 290 | -------------------------------------------------------------------------------- /old_news_and_organisation_posts/task_three.md: -------------------------------------------------------------------------------- 1 | category: Домашно 2 | 3 | tags: 4 | - elixir 5 | - homework 6 | 7 | -------- 8 | 9 | # Задача 3 10 | 11 | Третата задача е за **9 точки**. 12 | 13 | Крайният срок за предаване е 20:00 на 10.05.2017 г. 14 | 15 | ## Информация за задачата 16 | Ако все още не сте се записали за получаване на домашни, можете да го направите, като изпратите имейл до адрес **register@elixir-lang.bg**, който трябва да отговаря на следните условия: 17 | * В Относно (Subject) трябва да напишете **register**. 18 | * В съдържанието на мейла трябва да напишете: име и фамилия, факултетен номер и да сложите линк към GitHub акаунта ви. 19 | 20 | Ако сте записани, сте получили мейл с линк към заданието за новото домашно. То съдържа основната структура на проекта, в който тряба да работите. Свободни сте да създавате колкото искате допълнителни файлове, стига те да се намират в една от директориите `lib` или `test` (не пишете във вече съществуващите файлове в `test`). В репото ще има няколко базови теста, които да ви съпътстват при писането на задачата (Пускайте тестовете често, за да следите прогреса си). Често "къмитвайте" промените във вашето локално хранилише, също така често "пушвайте" към съответното в GitHub. Ще се постараем да върнем обратна връзка в рамките на един ден (ако не получите такава, може да пишете мейл на **course@elixir-lang.bg**, за да ни напомните). Всички работи, предадени в последния ден от срока за предаване, няма да получат обратна връзка, преди да бъдат окончателно проверени. 21 | 22 | ## Table 23 | 24 | Задачата ви е да направите модул **Table**, който да подържа работа с таблици. 25 | 26 | ### Какво представлява една таблица 27 | 28 | Всяка таблица е правоъгълна с произволен брой клетки. Всяка клетка съдържа число, стринг или нищо (`nil`). 29 | 30 | ### Интерфейса за работа с таблици 31 | 32 | #### Table.new() 33 | 34 | Създава празна таблица. 35 | 36 | #### Table.new(list) 37 | 38 | Създава таблица от списък от редове на таблицата (които на свой ред също са списък от списъци). 39 | 40 | ```elixir 41 | Table.new([[1, 2, 3], 42 | ["a", "b", "c"]]) 43 | ``` 44 | Таблица, създадена по горния начин, съответства на: 45 | 46 | | 1 | 2 | 3 | 47 | |-|-|-| 48 | | a | b | c | 49 | {: .table} 50 | 51 | ```elixir 52 | Table.new([[2], 53 | ["a", "b", "c"], 54 | ["ba", "ba"]]) 55 | ``` 56 | 57 | | 2 | | | 58 | |-|-|-| 59 | | a | b | c | 60 | | ba | ba | | 61 | {: .table} 62 | 63 | ```elixir 64 | iex> Table.new([]) == Table.new() 65 | true 66 | ``` 67 | 68 | #### Table.size(table) 69 | 70 | Тази функция връща наредена двойка `{rows, columns}`, съответно броят на редовете и броят на колоните в таблицата. 71 | 72 | #### Table.to_list(table) 73 | 74 | Тази функция трябва да връща списък от списъци, където всеки вътрешен списък представлява ред в таблицата. 75 | 76 | Ако `table` има следния вид: 77 | 78 | | 1 | 2 | | 79 | |-|-|-| 80 | | 3 | 4 | 5 | 81 | | | 6 | | 82 | {: .table} 83 | 84 | ```elixir 85 | iex> Table.to_list(table) 86 | [[1, 2, nil], [3, 4, 5], [nil, 6, nil]] 87 | ``` 88 | 89 | Ако таблицата е празна функцията трябва да върне празен списък. 90 | 91 | #### Table.insert_row(table, row \\\\ [], position \\\\ :last) 92 | 93 | Създава нова таблица с добавен нов ред `row`, като реда е вмъкнат на позиция `position` 94 | - `row` е списък с елементи. Ако новият ред има повече елементи от броя на колоните на таблицата, то колоните трябва да бъдат увеличени така, че да може да се запише целия нов ред. 95 | - `position` е цяло число или атома `:last` 96 | - ако е зададено число редът се вмиква на съответната позиция 97 | - ако числото е по-малко или равно на 1, редът бива вмъкнат в началото на таблицата 98 | - ако числото е по-голямо от досегашния брой редове, или e atoma `:last`, новият ред се вмъква в края на таблицата 99 | 100 | За следващите няколко примера таблицата (`table`), която ще изпозваме като входна за функцията има следния вид: 101 | 102 | |1|2| 103 | |-|-| 104 | |3|4| 105 | {: .table} 106 | 107 | ```elixir 108 | Table.insert_row(table) 109 | ``` 110 | 111 | Този код създава следната таблица: 112 | 113 | |1|2| 114 | |-|-| 115 | |3|4| 116 | | | | 117 | {: .table} 118 | 119 | ```elixir 120 | Table.insert_row(table, [5,6]) 121 | 122 | Table.insert_row(table, [5,6], :last) 123 | 124 | Table.insert_row(table, [5,6], 6) 125 | ``` 126 | 127 | Тези три извиквания на функцията са еквивалентни и "произвеждат" следната таблица: 128 | 129 | | 1 | 2 | 130 | |-|-| 131 | | 3 | 4 | 132 | | 5 | 6 | 133 | {: .table} 134 | 135 | ```elixir 136 | Table.insert_row(table, [5,6], -10) 137 | 138 | Table.insert_row(table, [5,6], 1) 139 | ``` 140 | 141 | | 5 | 6 | 142 | |-|-| 143 | | 1 | 2 | 144 | | 3 | 4 | 145 | {: .table} 146 | 147 | ```elixir 148 | Table.insert_row(table, [5,6], 2) 149 | ``` 150 | 151 | | 1 | 2 | 152 | |-|-| 153 | | 5 | 6 | 154 | | 3 | 4 | 155 | {: .table} 156 | 157 | Функцията може да бъде извикана с по-голям или по-малък ред от досегашната големина на таблицата тогава поведението е следното: 158 | 159 | 160 | ```elixir 161 | Table.insert_row(table, [5,6,7]) 162 | ``` 163 | 164 | | 1 | 2 | | 165 | |-|-|-| 166 | | 3 | 4 | | 167 | | 5 | 6 | 7 | 168 | {: .table} 169 | 170 | ```elixir 171 | Table.insert_row(table, [5]) 172 | ``` 173 | 174 | | 1 | 2 | 175 | |-|-| 176 | | 3 | 4 | 177 | | 5 | | 178 | {: .table} 179 | 180 | #### Table.insert_column(table, column \\\\ [], positon \\\\ :last) 181 | 182 | Поведението на `insert_column` е аналогично на това на `insert_row`, но за колони. 183 | 184 | #### Table.delete_row(table, positon \\\\ :last) 185 | 186 | Премахва ред от таблицата на позиция `position`, като връща наредена двойка `{new_table, deleted_row}`, съответно новата таблица и изтрития ред като списък. 187 | - `position` e цяло число или атома `:last` 188 | - ако е подаденo число, което не отговаря на никой ред тогава функцията трябва да върне старата таблица и празен списък. 189 | - ако е подаден атомът `:last`, трябва да бъде изтрит последният ред. 190 | 191 | За следващите няколко примера таблицата (`table`), която ще изпозваме като входна за функцията има следния вид: 192 | 193 | | 1 | 2 | | 194 | |-|-|-| 195 | | 3 | 4 | 5 | 196 | | | 6 | | 197 | {: .table} 198 | 199 | ```elixir 200 | iex> {new_table, row} = Table.delete_row(table); row 201 | [nil, 6, nil] 202 | iex> {new_table, row} = Table.delete_row(table, 3); row 203 | [nil, 6, nil] 204 | iex> {new_table, row} = Table.delete_row(table, :last); row 205 | [nil, 6, nil] 206 | ``` 207 | 208 | Таблицата `new_table` трябва да има следния вид и след извикването и на трите функции: 209 | 210 | | 1 | 2 | | 211 | |-|-|-| 212 | | 3 | 4 | 5 | 213 | {: .table} 214 | 215 | ```elixir 216 | iex> {new_table, row} = Table.delete_row(table, 2); row 217 | [3, 4, 5] 218 | ``` 219 | 220 | `new_table` е: 221 | 222 | | 1 | 2 | | 223 | |-|-|-| 224 | | | 6 | | 225 | {: .table} 226 | 227 | ```elixir 228 | iex> {^table, row} = Table.delete_row(table, 0); row 229 | [] 230 | iex> {^table, row} = Table.delete_row(table, 10); row 231 | [] 232 | ``` 233 | 234 | #### Table.delete_column(table, positon \\\\ :last) 235 | 236 | Поведението на `delete_column` е аналогично на това на `delete_row`, но за колони. 237 | 238 | #### Table.cell(table, {row, column}) 239 | 240 | Връща стойността в клетката, съответстваща на ред `row` и колона `column` (и двете трябва да са цели числа). Ако клетката е извън текущия размер на таблицата се връща `nil`. 241 | 242 | #### Table.overwrite_cell(table, {row, column}, value \\\\ nil) 243 | 244 | Презаписва се стойността на клетката, съответстваща на ред `row` и колона `column` (и двете трябва да са цели числа). Ако такава клетка не съществува, тогава се връща старата таблица. 245 | - `value` е стринг, число, `nil` или функция 246 | - ако е стринг, число или `nil`, то това е новата стойност на клетката. 247 | - ако е функция то стойността ѝ се изчислява и това е новата стойност на клетката (ако тя е валидна такава). 248 | 249 | ### Експортиране на таблица 250 | 251 | Вашата таблица трябва да може да бъде "записана" в 3 различни формата: CSV, HTML и Markdown. За това ще се грижи функцията `Table.format_as(table, format)`. Функцията връща `IOList` 252 | -`format` е един от атомите: `:md`, `:html` или `:csv` 253 | 254 | #### Експортиране в CSV формат. 255 | 256 | CSV(Comma-separated values) форматът може да се използва за да се представи таблица. В този формат всеки ред от таблицата е изобразен в ред от файла, а отделните клетки в даден ред са отделени със запетаи. Например следната таблица: 257 | 258 | |1|2.0|three| 259 | |-|-|-| 260 | | Are | you | counting? | 261 | {: .table} 262 | 263 | би изглеждала така във csv формат 264 | 265 | ``` 266 | 1,2.0,three 267 | Are,you,counting? 268 | ``` 269 | 270 | Важно е да се отбележи, че в csv формата има 3 специални символа: `"`, `,` и `\n`. Ако искаме някоя клетка да съдържа тези символи, то информацията за тази клетка трябва да започва и да завършва със `"`, а всички срещания на `"` вътре в клетката да бъдат заменени с `""`. Клетка също може да бъде празна. Например: 271 | 272 | | 1, 2.0, three || 273 | |-|-| 274 | || "Are you counting?" He said | 275 | {: .table} 276 | 277 | би изглеждала така: 278 | 279 | ``` 280 | "1, 2.0, three", 281 | ,"""Are,you,counting?"" 282 | He said" 283 | ``` 284 | 285 | #### Експортиране в HTML формат. 286 | 287 | Всички знаем какво е HTML. Затова направо към примера. 288 | 289 | | 1 | 2.0 | three | 290 | |-|-|-| 291 | | Are | you | counting? | 292 | {: .table} 293 | 294 | би изглеждала така във HTML формат: 295 | 296 | ``` 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 |
12.0three
Areyoucounting?
309 | ``` 310 | Може да не отделяте отделяте време на форматиране. Вариант, в който всичко е представено на един ред също върши работа - важното е да е валидна HTML таблица. 311 | 312 | Във HTML също има символи, които трябва да бъдат представени по друг начин: 313 | - `<` става `<` 314 | - `>` става `>` 315 | - `&` става `&` 316 | 317 | #### Експортиране в MD формат. 318 | 319 | Последният вариант за форматиране на таблица е Markdown. Нека да видим как изглежда една таблица в MD: 320 | 321 | | 1 | 2.0 | three | 322 | |-|-|-| 323 | | Are | you | counting? | 324 | | Yes | No | Maybe | 325 | {: .table} 326 | 327 | Би изглеждало така: 328 | 329 | ``` 330 | |1|2.0|three| 331 | |-|-|-| 332 | |Are|you|counting?| 333 | |Yes|No|Maybe| 334 | ``` 335 | 336 | Трябва да забележим, че втория ред винаги се състои от `|` разделени с `-`. 337 | 338 | В MD трябва да замените същите неща, както в HTML: 339 | - `<` става `<` 340 | - `>` става `>` 341 | - `&` става `&` 342 | 343 | Също така всеки от символите: `\` `*` `_` `{` `}` `|` `[` `]` `(` `)` `#` `+` `-` `.` и `!` трябва да пъде предшестван от `\`. Например: 344 | 345 | 346 | | a > b | \|\| | \(arr\[4\] & 1\) == 0 | && | p \- 7 | 347 | |-|-|-|-|-| 348 | | Are | those | some | magic | incantations? | 349 | {: .table} 350 | 351 | Ще изглежда така като стринг: 352 | 353 | ``` 354 | |a>b|\|\||\(arr\[4\]&1\)==0|&&|p\-7| 355 | |-|-|-|-|-| 356 | |Are|those|some|magic|incantations?| 357 | ``` 358 | -------------------------------------------------------------------------------- /old_news_and_organisation_posts/task_two_2019.md: -------------------------------------------------------------------------------- 1 | --- 2 | category: Домашно 3 | created_at: 2018-05-16T10:03:25 4 | author: Reductions 5 | tags: 6 | - elixir 7 | - homework 8 | --- 9 | 10 | # Задача 2 11 | 12 | Задачата е за общо **15 точки**. 13 | 14 | Крайният срок за предаване е 23:59 на 04.06.2018 г. 15 | 16 | Ако имате въпрос по домашното пишете на: course@elixir-lang.bg 17 | 18 | Ето и линк към заданието: 19 | 20 | https://classroom.github.com/a/vXu6wVow 21 | 22 | ## Lambda 23 | 24 | В предоставенто ви репо има начален скелет на **OTP application** с име `Lambda.Application`. За реализацията на функционалността описана по-долу можете да правите всякакви променяте в **Супервижън дървото** на тази апликация. Апликацията трябва да реализира функционалност за изпълнението на функция с дадени аргументи и да кешира резултата от нея. При всяко следващо извикване на кеширана функция резултатът не трябва да бъде пресмятан отново, а да се връща кеширания резултат. 25 | 26 | Прочетете цялата задача преди да почнете да пишете по нея, включително забележката в края на документа. 27 | 28 | ### Интерфейс 29 | 30 | Част от следващите функции трябва да са синхронни, а останалите асинхронни. Ще ги бележа съответно с **[call]** и **[cast]**. 31 | 32 | #### Lambda.register(module, function) [cast] 33 | 34 | Тази функция казва на апликация, че от тук нататък може да очаква извикването на функцията `function` от модула `module`. 35 | 36 | Тук точно като при `spawn/3` `module` и `function` са атоми. 37 | 38 | #### Lambda.run(module, function, arguments, timeout \\ 5_000) [call] 39 | 40 | Изпълнява функцията `function` от модула `module` с аргументи `arguments` (списък от аргументите на функцията) 41 | - ако функцията не е регистрирана преди това, отговора на сървъра трябва да е `{:error, :not_registered}` 42 | - ако функцията се run-ва за първи път с тези аргументи тя се изпълнява и се връща резултата във наредена двойка `{:ok, result}` 43 | - ако функцията бива изпълнена повторно с тези аргументи, връщаме кешираната стойност отново във вида `{:ok, result}` 44 | 45 | За да можем да разгледаме няколко примера нека си представим, че имаме следния модул: 46 | 47 | ```elixir 48 | defmodule Test do 49 | def wait(time) when time > 0 and is_integer(time) do 50 | Process.wait(time) 51 | time 52 | end 53 | 54 | def crasher() do 55 | raise "I will crash you!" 56 | end 57 | end 58 | ``` 59 | 60 | Тогава: 61 | 62 | ```elixir 63 | iex> Lambda.run(Test, :wait, [1]) 64 | {:error, :not_registered} 65 | iex> Lambda.register(Test, :wait) 66 | :ok 67 | iex> Lambda.run(Test, :wait, [1]) 68 | {:ok, 1} 69 | iex> :timer.tc fn -> Lambda.run(Test, :wait, [1000]) end 70 | {1024969, {:ok, 1000}} 71 | iex> :timer.tc fn -> Lambda.run(Test, :wait, [1000]) end 72 | {36, {:ok, 1000}} 73 | ``` 74 | 75 | Както можете да видите от примера, вторият път, когато извикаме `Test.wait` с аргумента `1000`, функцията изобщо не се изпълнява, а просто се връща резултата от предходното изпълнение. Затова извикването завършва много по-бързо втория път (изчакването `Process.wait(1000)` не се случва). 76 | 77 | Ако функцията не успее да завърши за `timeout` трябва да върнем :timeout (това не би трябвало да се случва в случай, че резултата е кеширан) 78 | 79 | ```elixir 80 | iex> Lambda.run(test, :wait, [10_000]) 81 | :timeout 82 | iex> Lambda.run(test, :wait, [10_000], 100_000) 83 | {:ok, 10_000} 84 | iex> Lambda.run(test, :wait, [10_000]) 85 | {:ok, 10_000} 86 | ``` 87 | 88 | Ако при извикване на `run` възникне грешка от каквото и да е естество, резултатът от функцията трябва да е `{:error, :execution_error}`. 89 | 90 | ```elixir 91 | iex> Lambda.register(Test, :crasher) 92 | :ok 93 | iex> Lambda.run(Test, :crash, []) 94 | {:error, :execution_error} 95 | ``` 96 | 97 | #### Lambda.fetch_cached(module, function, arguments) [call] 98 | 99 | Проверява дали има кеширан резултат от изпълнението на функцията `function` от модула `module` с аргументи `arguments`: 100 | - ако функцията не е регистрирана преди това, отговорът трябва да е `{:error, :not_registered}` 101 | - ако резултатът от функцията не е кеширан, за тези аргументи се връща `{:error, :not_cached}` 102 | - ако резултатът е кеширан се връщa `{:ok, result}` 103 | 104 | 105 | ```elixir 106 | iex> Lambda.fetch_cached(Test, :wait, [1]) 107 | {:error, :not_registered} 108 | iex> Lambda.register(Test, :wait) 109 | :ok 110 | iex> Lambda.fetch_cached(Test, :wait, [1]) 111 | {:error, :not_cached} 112 | iex> Lambda.run(Test, :wait, [1]) 113 | {:ok, 1} 114 | iex> Lambda.fetch_cached(Test, :wait, [1]) 115 | {:ok, 1} 116 | ``` 117 | 118 | #### Lambda.clear_cache(module, function) [cast] 119 | 120 | Трябва да изчиства цялата кеширана информация за функцията `function` от модула `module`. Ако тя е била регистрирана преди това, то тя трябва да остане такава и след това. Ако не е била регистрирана не се случва нищо. 121 | 122 | 123 | ```elixir 124 | iex> Lambda.register(Test, :wait) 125 | :ok 126 | iex> Lambda.run(Test, :wait, [1]) 127 | {:ok, 1} 128 | iex> Lambda.fetch_cached(Test, :wait, [1]) 129 | {:ok, 1} 130 | iex> Lambda.clear_cache(Test, :wait) 131 | :ok 132 | iex> Lambda.fetch_cached(Test, :wait, [1]) 133 | {:error, :not_cached} 134 | ``` 135 | 136 | #### Lambda.clear_cache(modlue, function, arguments) [cast] 137 | 138 | Трябва да изчиства кешираната информация за функцията `function` от модула `module` при аргументи `arguments`. 139 | 140 | ```elixir 141 | iex> Lambda.register(Test, :wait) 142 | :ok 143 | iex> Lambda.run(Test, :wait, [1]) 144 | {:ok, 1} 145 | iex> Lambda.run(Test, :wait, [2]) 146 | {:ok, 2} 147 | iex> Lambda.clear_cache(Test, :wait, [1]) 148 | :ok 149 | iex> Lambda.fetch_cached(Test, :wait, [1]) 150 | {:error, :not_cached} 151 | iex> Lambda.fetch_cached(Test, :wait, [2]) 152 | {:ok, 2} 153 | ``` 154 | 155 | #### Lambda.unregister(module, function) [cast] 156 | 157 | Тази функция казва на апликацията, че не се очаква извикване на функцията `function` от модула `module` и че не е необходимо да се съхранява повече информацията за функцията. 158 | 159 | ```elixir 160 | iex> Lambda.register(Test, :wait) 161 | :ok 162 | iex> Lambda.run(Test, :wait, [1]) 163 | {:ok, 1} 164 | iex> Lambda.unregister(Test, :wait) 165 | :ok 166 | iex> Lambda.run(Test, :wait, [1]) 167 | {:error, :not_registered} 168 | iex> Lambda.register(Test, :wait) 169 | :ok 170 | iex> Lambda.fetch_cached(Test, :wait, [1]) 171 | {:error, :not_cached} 172 | ``` 173 | 174 | ### Забележка 175 | 176 | За получаване на пълния брой точки **Е НЕОБХОДИМО** апликацията ви да може да изпълнява повече от една функция по едно и също време. Показаното по-долу е правилното поведение: 177 | 178 | ```elixir 179 | iex> Lambda.register(Test, :wait) 180 | :ok 181 | iex> task = 182 | ...> fn(pid, seconds) -> 183 | ...> result = Lambda.run(Test, :wait, [seconds*1000], :infinity) 184 | ...> send(pid, result) 185 | ...> end 186 | #Function<...> 187 | iex> one_second_task = fn -> task(self(), 1) end 188 | #Function<...> 189 | iex> ten_second_task = fn -> task(self(), 10) end 190 | #Function<...> 191 | iex> Process.spawn(ten_second_task, []) 192 | #PID<0.123.0> 193 | iex> Process.spawn(one_second_task, []) 194 | #PID<0.126.0> 195 | iex> Process.sleep(8000) 196 | :ok 197 | iex> flush() 198 | {:ok, 1000} # По-бързата заявка е приключила първа и връща отговор. 199 | :ok 200 | iex> Process.sleep(8000) 201 | :ok 202 | iex> flush() 203 | {:ok, 10000} # По-бавната заявка завършва също и връща отговор. 204 | :ok 205 | ``` 206 | 207 | Друго нещо за което бихме тествали е, че ако в близко време еднакви заявкa пристигне два пъти, то тя ще бъде изпълнена само веднъж. 208 | 209 | ```elixir 210 | iex> defmodule Test do 211 | ...> def wait(time) do 212 | ...> IO.puts "Tra la la" 213 | ...> Process.sleep(time) 214 | ...> time 215 | ...> end 216 | iex> Lambda.register(Test, :wait) 217 | :ok 218 | iex> task = 219 | ...> fn(pid, seconds) -> 220 | ...> result = Lambda.run(Test, :wait, [seconds*1000], :infinity) 221 | ...> send(pid, result) 222 | ...> end 223 | #Function<...> 224 | iex> three_second_task = fn -> task(self(), 3) end 225 | #Function<...> 226 | iex> Process.spawn(three_second_task, []) 227 | #PID<0.123.0> 228 | iex> Process.spawn(three_second_task, []) 229 | #PID<0.126.0> 230 | iex> Process.sleep(5000) 231 | "Tra la la" 232 | :ok 233 | iex> flush() 234 | {:ok, 3000} 235 | {:ok, 3000} # И двете заявки са върнали отговор, но функцията е изпълнена само веднъж. 236 | :ok 237 | ``` 238 | 239 | Напомняме, че използването на `if`, `unless`, `cond`, `raise` и `throw` е забранено. За целта на това домашно ще трябва да спазвате правилото да не стартирате не наблюдавани процеси. 240 | -------------------------------------------------------------------------------- /old_news_and_organisation_posts/weekly_schedule.md: -------------------------------------------------------------------------------- 1 | --- 2 | category: Разписание 3 | tags: 4 | - elixir 5 | - schedule 6 | --- 7 | 8 | # Седмично разписание 9 | 10 | Тази година курсът ще е **два** пъти в седмицата, по **два** часа. 11 | 12 | Първата ни среща ще бъде на **20.02.2018, Вторник, Зала 02, 19:00 часа**. 13 | 14 | ## Разписание 15 | 16 | |Зала|Ден|Време| 17 | |:--:|:--:|:--:| 18 | |02 |Вторник |19:15 - 21:00| 19 | |101|Четвъртък|19:15 - 21:00| 20 | {: .table .table-striped .table-bordered .table-hover} 21 | -------------------------------------------------------------------------------- /posts/archive/binaries.md: -------------------------------------------------------------------------------- 1 | --- 2 | title_image_path: binaries.jpg 3 | category: Програма 4 | author: meddle 5 | created_at: 2017-03-21T12:00:00 6 | tags: 7 | - elixir 8 | - binaries 9 | - erlang 10 | - pattern matching 11 | - strings 12 | --- 13 | 14 | # Binaries 15 | 16 | Това е първата статия от серия на тема _Модули и структури от данни в Elixir_. 17 | Ще се запознаем по-задълбочено с двоичните структури в Elixir, как са представени и 18 | как и за какво биха могли да са ни полезни. 19 | 20 | ## Конструкция 21 | 22 | В общи линии една `binary` структура представлява поредица от байтове или битове. 23 | Дефинира се със следната конструкция: 24 | 25 | ```elixir 26 | <<>> 27 | ``` 28 | 29 | В тази конструкция можем да слагаме поредица от числа представляващи битове или 30 | байтове: 31 | 32 | ```elixir 33 | << 123, 23, 1 >> 34 | ``` 35 | 36 | Всяко число представлява един байт. В този ред на мисли, това е вярно: 37 | 38 | ```elixir 39 | << 0b01111011, 0b00010111, 0b00000001 >> == << 123, 23, 1 >> 40 | true 41 | ``` 42 | 43 | Числата винаги представляват един байт, ако числото е по-голямо от `255`, 44 | то е отрязано до един байт. 45 | 46 | ```elixir 47 | iex> << 280 >> 48 | <<24>> 49 | ``` 50 | 51 | Разбира се има начин числата да представляват повече от един байт. 52 | Това става с помощта на модификатора `size`. 53 | 54 | ```elixir 55 | iex> << 280::size(16) >> # Същото като << 280::16 >> 56 | << 1, 24 >> 57 | 58 | iex> << 0b00000001, 0b00011000 >> == << 280::16 >> 59 | true 60 | ``` 61 | 62 | С този модификатор можем да представим число в `7` бита, да речем: 63 | 64 | ```elixir 65 | << 128::7 >> 66 | 1 # Всъщност е отрязано от 10000001 - взимат се десните 7 бита - 0000001 67 | ``` 68 | 69 | Интересно е съхранението на числа с плаваща запетая като `binaries`. 70 | Винаги се представят като `binary` от 8 байта: 71 | 72 | ```elixir 73 | iex> << 5.5::float >> 74 | <<64, 22, 0, 0, 0, 0, 0, 0>> 75 | ``` 76 | 77 | Това е така, защото числата с плаваща запетая в Elixir са с _double_ прецизност или 78 | `64` битови. 79 | 80 | ## Функции и операции свързани с binaries 81 | 82 | ### Конкатенация 83 | 84 | Възможно е да конкатенираме binary структури с оператора `<>`. 85 | 86 | ```elixir 87 | iex> << 83, 79, 83 >> <> << 24, 4 >> 88 | <<83, 79, 83, 24, 4>> 89 | ``` 90 | 91 | Друг начин за конкатенация е: 92 | 93 | ```elixir 94 | << 83, 79, 83, << 24, 4 >> >> 95 | ``` 96 | 97 | Този код е аналогичен на по-горния, с `<>` оператора. 98 | 99 | ### Размер 100 | 101 | В паметта, за `binary` структурите, има мета-информация, която съхранява дължината им. 102 | Това значи че четенето на дължината на `binary` е бърза, константна операция: 103 | ```elixir 104 | iex> byte_size(<< 83, 79, 83 >>) 105 | 3 106 | ``` 107 | 108 | Когато боравим с битове, чийто брой не е кратен на `8`, ще се закръгли нагоре: 109 | ```elixir 110 | byte_size(<< 34::5, 23::2, 12::2 >>) 111 | 2 112 | 113 | # Между другото: 114 | << 34::5, 23::2, 12::2 >> == << 22, 0::1 >> 115 | true 116 | ``` 117 | 118 | Разбира се можем да видим и точната дължина в битове: 119 | ```elixir 120 | iex> bit_size(<< 34::5, 23::2, 12::2 >>) 121 | 9 122 | ``` 123 | 124 | ### Проверки 125 | 126 | Има два метода за проверяване на типа на променлива, свързани с `binary` структурите. 127 | 128 | 1. `is_bitstring` - Винаги е истина за каквато и да е валидна поредица от данни между `<<` и `>>`. Няма значение дължината върната от `bit_size`. 129 | 2. `is_binary` - Истина е само ако `bit_size` връща число кратно на `8` - тоест структурата е поредица от байтове. 130 | 131 | В повечето случаи ще използваме `is_binary`. 132 | 133 | ### Sub-binaries 134 | 135 | С функцията `binary_part`, можем да боравим с части от дадена `binary` структура: 136 | 137 | ```elixir 138 | iex> binary_part(<< 83, 79, 83, 23, 21, 12 >>, 1, 3) 139 | <<79, 83, 23>> 140 | ``` 141 | 142 | Взима под-структура от индекс - втория аргумент с брой елементи - третия. Очаквайте 143 | `ArgumentError`, ако тези аргументи са невалидни индекс и/или дължина за дадената структура. 144 | 145 | Важното тук е, че това е бърза операция - нищо не се копира, просто получавате указател 146 | от дадено място в паметта, с дадена мета-дължина. 147 | 148 | ## Pattern matching 149 | 150 | Като всичко друго в Elixir и с `binaries` можем да съпоставяме: 151 | 152 | ```elixir 153 | << x, y, x >> = << 83, 79, 83 >> 154 | 155 | x 156 | 83 157 | 158 | y 159 | 79 160 | ``` 161 | 162 | Друга интересна възможност е да съпоставим променлива към повече от един байт. 163 | 164 | ```elixir 165 | iex> << x, y, z::binary >> = << 83, 79, 83, 43, 156 >> 166 | <<83, 79, 83, 43, 156>> 167 | iex> z 168 | << 83, 43, 156 >> 169 | 170 | # Ако не го направим така, автоматично сапоставя променлива на 1 байт: 171 | iex> << x, y, z >> = << 83, 79, 83, 43, 156 >> 172 | ** (MatchError) no match of right hand side value: <<83, 79, 83, 43, 156>> 173 | ``` 174 | 175 | Ето пример за разделяне и изваждане на частите на число с плаваща запетая, 176 | така както е представено: 177 | 178 | ```elixir 179 | iex> << sign::size(1), exponent::size(11), mantissa::size(52) >> = << 4815.162342::float >> 180 | <<64, 178, 207, 41, 143, 62, 204, 196>> 181 | iex> sign 182 | 0 183 | ``` 184 | 185 | Тук знакът е `1` при отрицателно число и `0`, при положително - в случая `0`. 186 | Пази се в един бит. 187 | Цялата част на числото се съдържа в `11` бита, тя се пресмята така: **2exponent - 1023**. 188 | Цялото число може да се пресметне с **(-1)sign(1 + mantissa/252)2exponent - 1023**: 189 | 190 | ```elixir 191 | iex> :math.pow(-1, sign) * (1 + mantissa / :math.pow(2, 52)) * :math.pow(2, exponent - 1023) 192 | 4815.162342 193 | ``` 194 | 195 | Освен `float` и `binary` модификаторите, има `integer` модификатор, който се прилага 196 | автоматично ако никой друг не е използван. Модификатор `bytes` е аналогичен на `binary`. 197 | Модификаторите `bits` и `bitstrig` се използват за съпоставяне на бит-стринг съдържание - поредица 198 | от битове с неопределена дължина. 199 | 200 | ```elixir 201 | iex> << x::5, y::bits >> = << 225 >> 202 | <<225>> 203 | iex> x 204 | 28 205 | iex> y 206 | <<1::size(3)>> 207 | ``` 208 | 209 | Модификаторите `bitstrig` и `binary`, без дължина могат да се използват само в края 210 | на `binary` структурата. 211 | 212 | Останалите възможни модификатори са `utf8`, `utf16` и `utf32`, които са свързани с unicode. 213 | Ще се върнем към `utf8`, когато си говорим за низове в следващата статия. 214 | 215 | Интересно нещо свързано с модификаторите е, че `size` работи с тях. 216 | Да речем `size` задава дължина в битове, когато работим с `integer`, но ако работим 217 | с `binary` модификатор, `size` е в байтове: 218 | 219 | ```elixir 220 | iex> << x::binary-size(4), _::binary >> = << 83, 222, 0, 345, 143, 87 >> 221 | <<83, 222, 0, 89, 143, 87>> 222 | iex> x # 4 байта 223 | <<83, 222, 0, 89>> 224 | ``` 225 | 226 | ## Имплементация 227 | 228 | По принцип всеки процес в `Elixir` има собствен `heap`. За всеки процес различни 229 | структури от данни и стойности са съхранени в този `heap`. Когато два процеса си 230 | комуникират, съобщенията, които се изпращат между тях се копират между `heap`-овете им. 231 | 232 | Когато си говорим за `binaries`, обаче, има една голяма разлика с този модел. 233 | Ако `binary`-то е `64` **байта** или по-малко, то е съхранявано в `heap`-a на процеса си 234 | и се копира при размяна с друг процес, както е описано по-горе. 235 | Такива `binary` структурки наричаме **heap binaries**. 236 | 237 | Когато структурката ни е по-голяма от `64` **байта**, тя не се пази в `process heap`-a. 238 | Пази се в обща памет за всички процеси на даден `node`. В `process heap`-a се пази 239 | малко обектче, наречено **ProcBin**, което е указател към даденото `binary`, съхранено 240 | в общия `heap`. В този общ `heap`, `binary` структурка може да бъде сочена от множество такива 241 | `ProcBin` указатели от множество процеси. Пази се _reference counter_ за всеки от тези указатели. 242 | Когато той стане `0`, Garbage Collector-ът ще може да изчисти `binary`-то от общия `heap`. 243 | Такива `binary` структури наричаме **refc binaries**. 244 | 245 | Защо това е хубаво? Защото при по-големички двоични структури няма постоянно копиране 246 | между процесите и съществуват различни оптимизации. 247 | Разбира се трябва да се внимава с тези `refc binaries`. 248 | 249 | Като си говорим за имплементацията е хубаво да обърнем внимание на два вида специални указатели, 250 | свързани с `binary` структурите. 251 | 252 | Споменахе по-горе за `sub binary`. Като цяло това е специален указател който сочи към част 253 | от дадено `binary`. Няма копиране (защото всичко е `immutable`). 254 | По горе видяхме примери, които създават такива указатели - `binary_part` функцита, 255 | която вътрешно ползва `:erlang.split_binary`. Създаването на `sub binary` е 256 | евтина операция, но ако става въпрос за `refc binary`, _reference counter_-a се увеличава. 257 | 258 | Друг специален обект е `match context`-ът. Той е подобен на `sub binary`, но оптимизиран за 259 | `binary pattern matching`. Държи указател към двоичните данни в паметта, и когато нещо е `match`-нато, 260 | указателят се придвижва напред. Компилаторът отлага създаването на `sub binary` за всяка `match`-ната променлива, 261 | ако е възможно и преизползва един и същ `match context`. 262 | 263 | ### Отново конкатенация 264 | 265 | Да видим как работи конкатенацията, когато сме запознати с модела на пазене на `binary` структурите в паметта. 266 | Имаме следния код: 267 | 268 | ```elixir 269 | x = << 83, 222, 0, 89 >> 270 | y = << x, 225, 21 >> 271 | z = << y, 125, 156 >> 272 | a = << y, 15, 16, 19 >> 273 | ``` 274 | 275 | Нека обясним всеки ред в този пример. 276 | 277 | На първия ред виждаме, че `x` сочи към ново `binary`, което е `4` байта, следователно се създава в `heap`-a на текущия процес. 278 | 279 | Вторият ред представлява конкатенация. 280 | Към `x` се добавят още `2` байта и това ново `binary` се присвоява на `y`. 281 | Какво става с паметта? 282 | Операцията за добавяне всъщност създава ново `refc binary`. Така е имплементирана. 283 | В общата памет се заделя място с големина `max(2 * byte_zise(x), 256)`, в случая - `256` байта, нещо такова: 284 | 285 | ``` 286 | [4] -> |83|222|0|89| | | | | -> 256 байта 287 | ``` 288 | 289 | Този указател по-горе е създаден в `heap`-a на текущия процес, също `heap binary`-то за `x`, което 290 | беще създадено на първи ред, сега може да бъде изчистено от Garbage Collector-a. 291 | Байтовите, които трябва да се добавят са добавени в свободното пространство: 292 | 293 | ``` 294 | [4] -> |83|222|0|89|225|21| | | -> 256 байта 295 | [6] -^ 296 | ``` 297 | 298 | Какво става на 3-ти ред. Искаме да добавим към `y` още `2` байта и да присвоим на `z` резултата. 299 | Вижда се, че след байтовете на `y` има свободно място, и те се преизползват за `z`. Няма копиране, 300 | оптимизирана операция, защото всичко е `immutable`, от което следва че може да се преизползва всичко, когато е възможно: 301 | 302 | 303 | ``` 304 | [4] -> |83|222|0|89|225|21|125|156| | | | -> 256 байта 305 | [6] -^ 306 | [8] -^ 307 | ``` 308 | 309 | Сега става интересно. 310 | На 4-ти ред искаме да добавим към `y` `3` байта и да присвоим резултата към `a`. 311 | Сега след стойността на `y`, в паметта има данни и не можем да преизползваме пространство. 312 | Затова `run-time` системата, копира стойността на `y` на ново място в паметта (използва формулата по-горе) 313 | и добавя `3`-те нови байта там: 314 | 315 | ``` 316 | [4] -> |83|222|0|89|225|21|125|156| | | | -> 256 байта 317 | [6] -^ 318 | [8] -^ 319 | 320 | [9] -> |83|222|0|89|225|21|15|16|19| | | -> 256 байта 321 | ``` 322 | 323 | Тази оптимизация е възможна в доста малко случаи. Има операции, които карат паметта да 324 | стане компактна и да освободи неизползваните байтове, от което следва че подобна оптимизация при добавяне 325 | след такива операции е невъзможна. Такива операции са пращане на `binary`-то като съобщение между процеси, например. 326 | Също и създаването на `match context` - pattern matching. 327 | 328 | С този последен пример завършваме тази статия. 329 | Опитахме да ви покажем `binary` структурите в по-голяма дълбочина преди да говорим за низове. 330 | Почти всичко паказано не е специфично за `Elixir` и идва от `Erlang`. 331 | Следващата статия ще разгледа низовете в дълбочина - че си говорим за `unicode` и `utf8`, ще споменем 332 | някои от функциите в `String` модула. 333 | 334 | Не забравяйте, по горните представяния в паметта и възможни оптимизации са валидни за низовете в `Elixir`. 335 | -------------------------------------------------------------------------------- /posts/archive/exceptions.md: -------------------------------------------------------------------------------- 1 | --- 2 | title_image_path: explosion.jpg 3 | category: Програма 4 | created_at: 2017-04-13T21:33:54 5 | tags: 6 | - elixir 7 | - exceptions 8 | - throw 9 | - pattern matching 10 | - raise 11 | - try 12 | - catch 13 | --- 14 | 15 | # Грешки 16 | 17 | В `Elixir`, грешките са предназначени за ситуации, които никога не би трябвало да се случат 18 | при нормални обстоятелства. Това ще рече - ситуации при които зависим от нещо външно и то спре да работи, 19 | зависим от някакви файлове, а тях ги няма, като цяло когато имаме проблем с ресурсите и конфигурацията от които нашата програма 20 | зависи. 21 | Да речем грешен `input` от потребител не е такава ситуация. 22 | 23 | ## 'Вдигане' на грешка 24 | 25 | Грешка се 'вдига' с `raise`: 26 | 27 | ```elixir 28 | raise "Ужаст!" 29 | # (RuntimeError) Ужаст! 30 | ``` 31 | 32 | По подразбиране, ако не подадем тип на грешката на `raise`, тя е `RuntimeError`. 33 | Можем да вдигнем грешка и с тип, без съобщение: 34 | 35 | ```elixir 36 | raise RuntimeError 37 | # (RuntimeError) runtime error 38 | ``` 39 | 40 | Както и с тип на грешката и съобщение: 41 | 42 | ```elixir 43 | raise ArgumentError, message: "Грешка, брато!" 44 | # (ArgumentError) Грешка, брато! 45 | ``` 46 | 47 | `Elixir` идва с набор от грешки за различни случаи, които можете да видите в 48 | [документацията](https://hexdocs.pm/elixir) под 'EXCEPTIONS'. 49 | 50 | Грешките в `Elixir` не са препоръчителни за употреба. 51 | Имат славата на `GOTO`/спагети програмиране и наистина е хубаво да помислим дали има нужда от тях в дадена ситуация. 52 | 53 | Прието е функции, при които има проблем, да връщат `{:error, <проблем>}`, а ако се изпълняват с успех и 54 | имат резултат - `{:ok, <резултат>}`. 55 | Имената на функции, които биха могли да 'вдигнат' грешка, обикновено завършват на `!`. 56 | Да речем ако имаме `SomeModule.some_function/1`, която връща `{:ok, result}` или `{:error, reason}`, 57 | ако искаме да дефинираме същата, но при успех връщаща `result`, а при неуспех, 'вдигаща' грешка, 58 | ще я кръстим `SomeModule.some_function!/1`. Когато говорим за вход-изход ще видим, че функциите идващи с езика 59 | следват тази конвенция. 60 | 61 | ## Прихващане на грешка 62 | 63 | Можем да прихванем грешка, използвайки `try/rescue` блок: 64 | 65 | ```elixir 66 | try do 67 | 1 / 0 68 | rescue 69 | [RuntimeError, ArgumentError] -> 70 | IO.puts("Няма да стигнем до тук.") 71 | error in [ArithmeticError] -> 72 | IO.puts("На нула не се дели, #{error.message}") 73 | any_other_error -> 74 | IO.puts("Лошаво... #{any_other_error.message}") 75 | else 76 | IO.puts("Няма грешка.") 77 | after 78 | IO.puts("Finally!") 79 | end 80 | ``` 81 | 82 | Примерът по горе показва няколко вида прихващане. Прихващането пък си е `match`-ване. 83 | Първият пример е чрез списък от тип грешки, докато във втория, виждаме как от този списък 84 | да си вземем грешката в променлива. 85 | Накрая хващаме всички типове, които не сме описали досега в променливата `any_other_error`. 86 | 87 | Имаме и `after` клауза, която винаги ще се изпълни след като `try` функцията завърши, няма значение, 88 | дали е имало грешка или не. Другата интересна клауза в примера е `else` - тялото ѝ се изпълнява само 89 | ако не е възникнала грешка в тялото на `try`. 90 | 91 | ## Създаване на нови типове грешки 92 | 93 | Можем да създадем и нови типове грешки. Подобно на структурите, нова грешка се 94 | дефинира като част от модул: 95 | 96 | ```elixir 97 | defmodule VeryBadError do 98 | defexception message: "Лошо!!!" 99 | end 100 | ``` 101 | 102 | Сега можем да я 'вдигнем': 103 | 104 | ```elixir 105 | try do 106 | raise VeryBadError 107 | rescue 108 | error in VeryBadError -> 109 | IO.puts(inspect(error, structs: false)) 110 | end 111 | # %{__exception__: true, __struct__: VeryBadError, message: "Лошо!!!"} 112 | ``` 113 | 114 | Както виждате, грешките са структури с още едно тайно поле - `__exception__`. 115 | То има стойност `true`. Нищо особено. 116 | 117 | ## Throw/Catch 118 | 119 | Тези две конструкции НЕ ТРЯБВА да се ползват. Има библиотеки, които поради някакво 120 | стечение на обстоятелствата е възможно да ги ползват, но ги избягвайте. Ние ви ги 121 | показваме за да ви кажем - не ги ползвайте. 122 | 123 | С `throw` 'подхвърляме' стойност, която може да се 'хване' по-късно: 124 | 125 | ```elixir 126 | try do 127 | throw 5 128 | catch 129 | x -> IO.puts(x) 130 | end 131 | # 5 132 | ``` 133 | 134 | Показахме ви ги. Сега - забравете за тях и не ги ползвайте. 135 | 136 | ## Още няколко думи 137 | 138 | Тази статия е доста кратка. Идеята ѝ е да ни запознае с грешките в `elixir` и да ни 139 | каже - 'не ги ползвайте'. Два факта: 140 | 1. В кода на `mix` няма прихващане на грешки. 141 | 2. В кода на компилатора на `Elixir` има точно пет прихващания. Но все пак това е компилатор и там се случват доста магически неща. 142 | 143 | Ако започнете да създавате нов тип за грешки се замислете. 144 | 145 | В `Erlang/Elixir` кодът върви в специални изолирани процеси. 146 | Идеологията е - остави го да се счупи. 147 | Това е така защото тези процеси са наистина изолирани и не споделят състояние. 148 | Ако един падне, друг ще бъде вдигнат на негово място и така ако е имало проблем, възникнал `runtime`, 149 | той ще се изчисти. Няма защо ние да правим това. 150 | Ще си говорим за тези процеси в следващи статии, засега е важно да запомните, че писането на нови типове грешки 151 | е нещо, което `Elixir` програмистите като цяло НЕ правят. Същото се отнася и за 'прихващането' им. 152 | И без никакви `throw/catch`-ове! 153 | -------------------------------------------------------------------------------- /posts/archive/input_output.md: -------------------------------------------------------------------------------- 1 | --- 2 | title_image_path: input_output.gif 3 | category: Програма 4 | created_at: 2017-04-16T16:39:09 5 | tags: 6 | - elixir 7 | - IO 8 | - file 9 | - path 10 | - puts 11 | - read 12 | - write 13 | - stream 14 | --- 15 | 16 | # Вход-изход 17 | 18 | В тази статия ще се запознаем с функциите, свързани с четене и писане. 19 | Ще разгледаме няколко модула от стандартната библиотека, като `IO` и `File`. 20 | 21 | ## Изход с `IO.puts/2` и `IO.write/2` 22 | 23 | Досега ползвахме `IO.puts/2` за да извеждаме текст на стандартния изход. 24 | Може би се чудите защо написахме функцията като функция на два аргумента. 25 | Това е защото тя е такава, просто първият, `device` има стойност по 26 | подразбиране. Засега няма да разглеждаме тази стойност. 27 | Тя е свързана с комуникацията между процеси. 28 | Това което ще направим е да подадем други стойности. Примери: 29 | 30 | ```elixir 31 | IO.puts("По подразбиране пишем на стандартния изход.") 32 | IO.puts(:stdio, "Можем да го направим и така.") 33 | IO.puts(:stderr, "Или да пишем в стандартния изход за грешки.") 34 | ``` 35 | 36 | Всъщност `puts` се държи по същия начин като друга функция в `IO` - `write`. 37 | Разликата е, че `puts` слага нов ред след текста, който му е подаден. 38 | 39 | ```elixir 40 | IO.write(:stderr, "Това е грешка!") 41 | ``` 42 | 43 | ## Какво е `chardata` 44 | 45 | Както казахме, първият аргумент на `write` и `puts` е `device`. 46 | Вторият е нещо от тип `chardata`. 47 | Какво е `chardata`? 48 | В статията [Списъци и потоци](/posts/lists_streams_recursion) споменахме за `iolist`, по-познат като `iodata` в `Elixir`. 49 | Този тип е доста подобен. 50 | 51 | `Chardata` е: 52 | * Низ, да речем `"далия"`. 53 | * Списък от `codepoint`-и, да речем `[83, 79, 0x53]` или `[?S, ?O, ?S]` или `'SOS'`. 54 | * Списък от `codepoint`-и и низове - `[83, 79, 83, "mayday!"]`. 55 | * Списък от `chardata`, тоест списък от нещата в горните три точки : `[[83], [79, ["dir", 78]]]`. 56 | 57 | Подавайки `chardata` на функции като `IO.puts/2` и `IO.write/2`, можем да избегнем конкатенация на 58 | низове, което е винаги хубаво нещо. Така няма копиране в паметта, data-та се изпраща направо в целта си. 59 | Показахме ви как да изграждате `HTML` темплейти с този тип данни. 60 | Като цяло това е едно от чудесата на `Erlang/Elixir`, ползвайте го вместо конкатенирани или интерполирани низове, когато можете. 61 | 62 | В `IO` има функция, която трансформира `chardata` в низ. 63 | 64 | ```elixir 65 | IO.chardata_to_string([1049, [1086, 1091], "!"]) 66 | # "Йоу!" 67 | ``` 68 | 69 | ## Вход с `IO.read/2`, `IO.gets/2`, `IO.getn/2` и `IO.getn/3` 70 | 71 | Функцията `read` също взима `device` като първи аргумент (което е или атом, да речем `:stdio` или `PID` на процес). 72 | Вторият аргумент може да бъде: 73 | * Атомът `:all` - значи да се изчете всичко идващо от `device`-а, докато не се достигне `EOF`, тогава се връща празен низ. 74 | * Атомът `:line` - прочита се всичко до нов ред или `EOF`. При `EOF`, функцията връща `:eof`. 75 | * Цяло число, по голямо от нула - прочита толкова символа от `device` или колкото може преди да достигне `EOF`. 76 | 77 | Функцията връща прочетеното: 78 | 79 | ```elixir 80 | iex> IO.read(:line) 81 | Хей, Хей 82 | # "Хей, Хей\n" 83 | ``` 84 | 85 | Много подобна е и функцията `IO.gets/2`. Тя приема `prompt` като втори аргумент и чете до нов ред: 86 | 87 | ```elixir 88 | iex> IO.gets("Кажи нещо!\n") 89 | Кажи нещо! 90 | Нещо! 91 | # "Нещо!\n" 92 | ``` 93 | 94 | Двете `getn` функции прочитат брой байтове или `unicode codepoint`-и, в зависимост от типа на `device`-а. 95 | Когато говорим за файлове ще разгледаме как можем да отворим файл в различни `mode`-ове. 96 | 97 | ## Какво е `iodata` 98 | 99 | Подобно на `chardata`, `iodata` може да се дефинира като списък от `data`. 100 | За разлика от `chardata`, `iodata` списъкът е от цели числа които представляват байтове (0 - 255), 101 | `binary` с елементи със `size`, кратен на 8 (могат да превъртат) и такива списъци. 102 | 103 | Има функции, които боравят с `iodata` - `IO.binwrite` и `IO.binread`. 104 | Тези функции са по-бързи от не-`bin*` вариантите им. 105 | Не трансформират това което получават в `utf8`. 106 | 107 | В `IO` има две функции за боравене с `iodata`: 108 | 109 | ```elixir 110 | IO.iodata_length([1, 2|<<3, 4>>]) 111 | # 4 112 | ``` 113 | 114 | Връща дължината в байтове на `iodata`-та. 115 | 116 | ```elixir 117 | IO.iodata_to_binary([1, << 2 >>, [[3], 4]]) 118 | # <<1, 2, 3, 4>> 119 | ``` 120 | 121 | Трансформира `iodata` в `binary`. 122 | 123 | ## Файлове 124 | 125 | Модулът `File` съдържа функции за работа с файлове. Някои от тях ни позволяват да отваряме 126 | файловете за писане и четене. По подразбиране всички файлове се отварят в `binary mode` и 127 | функциите `IO.binwrite/2` и `IO.binread/2` трябва да се използват за работа с тях. 128 | 129 | Разбира се файл може да бъде отворен и в `utf8 mode`. По този начин байтовете, записани или прочетени, 130 | ще се интерпретират като `UTF8` codepoint-и. 131 | 132 | ```elixir 133 | {:ok, file} = File.open("test.txt", [:write]) 134 | # {:ok, #PID<0.855.0>} 135 | IO.binwrite(file, "some text!") 136 | File.close(file) 137 | ``` 138 | 139 | Както можем да видим, `File.open/2` връща наредена двойка - `{:ok, device}`. 140 | Ако имаше някаква грешка щяхме да получим `{:error, reason}`. 141 | Това е нормално при повечето функции свързани с файлове. Разбира се има и функции, 142 | които хвърлят грешка при проблем и връщат резултата направо. Те имат същите имена, но завършващи на `!`. 143 | Да речем `File.open!/2`. 144 | 145 | В модула има много функции за създаване и триене на файлове и директории, за проверки дали съществуват, 146 | за промяна и показване на съдържанието на директория. 147 | Прочетете за тях в [документацията](https://hexdocs.pm/elixir/1.0.5/File.html). 148 | 149 | ## Процеси и файлове 150 | 151 | Така нареченият `device` всъщност е `PID` на процес или атом, който сочи към `PID` на процес. 152 | По принцип, когато отваряме файл се създава нов процес, който знае `file descriptor`-а на файла 153 | и управлява писането и четенето към и от него. 154 | 155 | Това е много хубаво нещо. От една страна това означава, че `IO` функциите на един `node`, 156 | могат да четат/пишат файл на друг `node`, или един компютър да управлява файлове на друг. 157 | От друга, означава че можем да си създаваме лесно свои `device`-и, чрез процеси, които 158 | знаят какво съобщение да очакват. 159 | 160 | Разбира се това означава и, че всяка операция с файла минава през комуникация между процеси. 161 | Когато искаме оптимално писане/четене на файл, това не е плюс. 162 | 163 | Именно за това има функции, които направо работят с файлове, като `File.read/1`, `File.read!/1`, 164 | `File.write/3`, `File.write!/3`. 165 | 166 | Тези функции отварят файла и пишат/четат в/от него като една операция, след това го затварят. 167 | 168 | ## Потоци и файлове 169 | 170 | Ако не искаме да прочетем цял файл в паметта, можем да го отворим и да си направим поток към него: 171 | 172 | ```elixir 173 | {:ok, file} = File.open("program.txt", [:read]) 174 | # {:ok, #PID<0.82.0>} 175 | 176 | IO.stream(file, :line) 177 | |> Stream.map(fn line -> line <> "!" end) 178 | |> Stream.each(fn line -> IO.puts(line) end) 179 | |> Stream.run 180 | ``` 181 | 182 | Това ще прочете файла ред по ред, трансформирайки редовете и ще ги изведе на стандартния изход. 183 | Разбира се `IO.stream` има и `IO.binstream` версия. 184 | 185 | Ако искаме по бързо четене/писане, без преминаване през комуникация между процеси, ползваме 186 | `File.stream!`: 187 | 188 | ```elixir 189 | File.stream!(filename, read_ahead: 10_000) 190 | ``` 191 | 192 | По подразбиране, когато използваме `File.stream!`, файловете се отварят в `raw binary read_ahead mode`. 193 | Това ще рече, че няма трансформация към `UTF8 codepoint`-и има буфериране в паметта. В примера по горе, 194 | показваме как можем да зададем големина на буфера. 195 | 196 | Ако искаме наистина бързо четене от файл на части, трансформиране и записване в друг файл 197 | е добре да следваме следния шаблон: 198 | 199 | ```elixir 200 | File.stream!(, read_ahead: ) 201 | |> Stream. 202 | ... 203 | |> Stream.into(File.stream!(, [:delayed_write])) 204 | |> Stream.run 205 | ``` 206 | 207 | По този начин комбинирайки `read_ahead` и `delayed_write` се получава буфериране с 208 | добра скорост. Повече по темата [тук](http://cloudless.studio/articles/12-elixir-vs-ruby-file-i-o-performance-updated). 209 | 210 | ## Модула IO.ANSI 211 | 212 | Този модул съдържа функции които контролират цвета, и форматирането в теминала. 213 | Много добре се комбинират в `chardata` списък с текст: 214 | 215 | ```elixir 216 | IO.puts [IO.ANSI.blue(), "text", IO.ANSI.reset()] 217 | # Ще отпечата 'text' в синьо, ако терминалът ви поддържа ANSI цветове 218 | ``` 219 | 220 | ## Модула StringIO и файлове в паметта 221 | 222 | Използвайки този модул, ние можем да четем/пишем от/в низове в паметта: 223 | 224 | ```elixir 225 | {:ok, pid} = StringIO.open("data") 226 | #PID<0.136.0>} 227 | 228 | StringIO.contents(pid) 229 | # {"data", ""} 230 | 231 | IO.write(pid, "doom!") 232 | #:ok 233 | 234 | StringIO.contents(pid) 235 | # {"data", "doom!"} 236 | 237 | IO.read(pid, :line) 238 | # "data" 239 | 240 | StringIO.contents(pid) 241 | # {"", "doom!"} 242 | 243 | StringIO.close(pid) 244 | # {:ok, {"", "doom!"}} 245 | ``` 246 | 247 | Както виждаме в паметта се държат два низа - един за вход, един за изход. 248 | Можем да четем от изхода, докато стане празен и да пишем във входа. 249 | 250 | Това не е точно поведението при един истински файл, за който нямаме две пространства, а само едно. 251 | 252 | Ако искаме псевдо-файл в паметта, който се държи като истински файл, можем да го направим така: 253 | 254 | ```elixir 255 | File.open("data", [:ram]) 256 | # {:ok, {:file_descriptor, :ram_file, #Port<0.1578>}} 257 | IO.binread(file, :all) 258 | # "data" 259 | ``` 260 | 261 | Опцията при отваряне `ram`, създава файл в паметта със съдържание първия аргумент на функцията `open`. 262 | Ако сега направим: 263 | 264 | ```elixir 265 | IO.binread(file, :all) 266 | # "" 267 | ``` 268 | 269 | Ще получим празен низ. Това е защото сме в края на файла, можем да променим това, с `Erlang` функцията `:file.postion/2`. 270 | 271 | ```elixir 272 | :file.position(file, :bof) 273 | IO.binread(file, :all) 274 | # "data" 275 | ``` 276 | 277 | Така отиваме в `:bof` - `beginning of file` и четем. В `Elixir` няма `random access` функции, но могат да се ползват 278 | тези от `erlang`. 279 | 280 | ## Модула Path 281 | 282 | Много от функциите във `File` изискват пътища. Модулът `Path`, ни предоставя спомагателни функции за работа с пътища. 283 | Примери: 284 | 285 | ```elixir 286 | Path.join("some", "path") 287 | # "some/path" 288 | Path.expand("~/development") 289 | # "/home/meddle/development" 290 | ``` 291 | 292 | По добре е да си строим пътищата с функции от `Path`. 293 | Те се справят с различията в операционните системи - знаят на какво вървят. 294 | 295 | ## Заключение 296 | 297 | Това беше всичко от нас за файловете. Тази статия е последната преди навлизането в 298 | процесите и по `advanced` темите. 299 | 300 | Научихме за структурите от данни в `Elixir`, типовете, `controw flow` конструкциите, грешките, работата с файлове. 301 | Време е да разберем по какъв начин работят процесите в които върви кодът ни и как си комуникират те. 302 | Това ще направим в следващата статия. 303 | -------------------------------------------------------------------------------- /posts/archive/processes_and_state.md: -------------------------------------------------------------------------------- 1 | --- 2 | title_image_path: wrapper.jpg 3 | category: Програма 4 | created_at: 2017-04-21T21:04:15 5 | tags: 6 | - elixir 7 | - state 8 | - agent 9 | - wrapper 10 | - recursion 11 | - start_link 12 | - stop 13 | --- 14 | 15 | # Процеси и състояние 16 | 17 | Всеки процес в еликсир изпълнява код, написан на функционален език. 18 | Този код е последователен. 19 | Самите процеси, обаче, приличат на актьорите в `Actor` модела. 20 | Те са компоненти, които си комуникират чрез съобщения. 21 | 22 | Можем ли да разглеждаме един процес като контейнер на някакво състояние, което 23 | можем да четем или променяме? 24 | 25 | Отговорът е, че можем. И тъй като вътрешността на процесите е функционална, това може да се постигне с рекурсия. 26 | В тази статия ще разгледаме процесите като контейнери на данни. 27 | 28 | ## Как да направим от процес контейнер на данни? 29 | 30 | Нека имаме следния модул - `Wrapper`: 31 | 32 | ```elixir 33 | defmodule Wrapper do 34 | def start_link(initial), do: spawn_link(fn -> run(initial) end) 35 | 36 | defp run(value) do 37 | receive do 38 | {:get, pid} when is_pid(pid) -> 39 | send(pid, {:ok, value}) 40 | run(value) 41 | _ -> run(value) 42 | end 43 | end 44 | end 45 | ``` 46 | 47 | Модулът `Wrapper` има само една публично-достъпна функция - `start_link/1`. 48 | Нейният параметър е стойността, която се съхранява в процес-опаковка. 49 | 50 | Функцията `run` е скрита, за да не замърсява публичния интерфейс. 51 | Именно защото е скрита не можем да ползваме `spawn_link/3`. 52 | Използваме `spawn_link` вместо `spawn` с идеята, че кодът на процесът, който използва 53 | `Wrapper` процес е тясно свързан с него. Ако `Wrapper` процесът се счупи, очакваме и кодът, 54 | който си комуникира с него да се счупи. 55 | 56 | Какво прави функцията `run/1`? 57 | Всъщност тя държи стойността, като си я предава с рекурсия. 58 | Тъй като винаги последното действие в `run` е извикване на `run`, получаваме `tail call` оптимизацията. 59 | 60 | Процес създаден с `Wrapper.start_link/1`, слуша за съобщение `{:get, }` и 61 | си връща състоянието във формат `{:ok, }`. Това може да се тества така: 62 | 63 | ```elixir 64 | pid = Wrapper.start_link(5) 65 | send(pid, {:get, self()}) 66 | 67 | receive do 68 | msg -> IO.inspect(msg) 69 | end 70 | # {:ok, 5} 71 | ``` 72 | 73 | Също така този процес не задържа съобщения, които не разбира: 74 | 75 | ```elixir 76 | pid = Wrapper.start_link(5) 77 | send(pid, :stuff) 78 | :timer.sleep(100) # Чакаме малко, за да сме сигурни, че съобщението е обработено 79 | 80 | {:messages, messages} = Process.info(pid, :messages) 81 | Enum.count(messages) == 0 82 | # true 83 | ``` 84 | 85 | И това е. Този процес ще си живее вечно, защото рекурсията създава безкраен цикъл. 86 | Стойността се предава от извикване на извикване на `run`, така че тя става опаковано състояние. 87 | 88 | ## Синхронно взимане на състоянието 89 | 90 | Лесно е да направим така, че стойността на `Wrapper` да може да се взима синхронно. 91 | Добавяме функция, която се справя със `send` и `receive` вместо нас: 92 | 93 | ```elixir 94 | def get(pid) do 95 | send(pid, {:get, self()}) 96 | 97 | receive do 98 | {:ok, value} -> {:ok, value} 99 | end 100 | end 101 | ``` 102 | 103 | Сега можем да проверим дали всичко работи добре: 104 | 105 | ```elixir 106 | pid = Wrapper.start_link(5) 107 | Wrapper.get(pid) 108 | # {:ok, 5} 109 | ``` 110 | 111 | Това което липсва е код, който се справя с грешки. 112 | С текущия код, ако процесът `Wrapper` 'умре', и текущият процес ще 'умре'. 113 | Те са свързани. И все пак текущият процес може да е системен, или може да се 114 | получи дълго чакане на резултата. Ето една версия на `get`, която връща резултат при грешка: 115 | 116 | ```elixir 117 | def get(pid, timeout \\ 5000) do 118 | send(pid, {:get, self()}) 119 | 120 | receive do 121 | {:ok, value} -> {:ok, value} 122 | anything -> {:error, anything} 123 | after 124 | timeout -> {:error, "Timeout"} 125 | end 126 | end 127 | ``` 128 | 129 | ## Промяна на стойността 130 | 131 | Искаме да можем да променяме стойността в `Wrapper` процеса. Това, разбира се, 132 | може да стане със съобщение. 133 | 134 | ```elixir 135 | receive do 136 | {:get, pid} when is_pid(pid) -> 137 | send(pid, {:ok, value}) 138 | run(value) 139 | {:set, new_value, pid} when is_pid(pid) -> 140 | send(pid, {:ok, new_value}) 141 | run(new_value) 142 | _ -> 143 | run(value) 144 | end 145 | ``` 146 | 147 | По този начин можем да променим стойността. Ето и тест за това: 148 | 149 | ```elixir 150 | pid = Wrapper.start_link(5) 151 | send(pid, {:set, 4, self()}) 152 | receive do 153 | msg -> IO.inspect(msg) 154 | end 155 | # {:ok, 4} 156 | 157 | send(pid, {:get, self()}) 158 | receive do 159 | msg -> IO.inspect(msg) 160 | end 161 | # {:ok, 4} 162 | ``` 163 | 164 | Можем да направим синхронна версия: 165 | 166 | ```elixir 167 | def set(pid, new_value, timeout \\ 5000) do 168 | send(pid, {:set, new_value, self()}) 169 | 170 | receive do 171 | {:ok, value} -> {:ok, value} 172 | anything -> {:error, anything} 173 | after 174 | timeout -> {:error, "Timeout"} 175 | end 176 | end 177 | ``` 178 | 179 | Сега можем да използваме тази функция за променяне на стойността. 180 | 181 | Частта с `receive` от `Wrapper.get/2` функцията и новата функция са еднакви. 182 | В такъв случай е добре да сложим този код в една функция, която да се извиква 183 | и от `set` и от `get`. По този начин ще преизползваме код. 184 | 185 | ## Промяна на стойността, използвайки текущата стойност 186 | 187 | Ще е хубаво да можем да променяме стойността на `Wrapper` процесите, използвайки 188 | текущите им стойности. Проблемът е, че ако използваме `get`, а после `set`, някой 189 | друг процес може вече да е променил стойността и нашият да я презапише. 190 | Това е типичен `race condition` и е добре да има начин да го заобиколим. 191 | Именно затова ще добавим нов вид съобщение, което третира `get` и `set` като 192 | едно атомарно действие: 193 | 194 | ```elixir 195 | receive do 196 | {:get, pid} when is_pid(pid) -> 197 | send(pid, {:ok, value}) 198 | run(value) 199 | {:set, new_value, pid} when is_pid(pid) -> 200 | send(pid, {:ok, new_value}) 201 | run(new_value) 202 | {:get_and_update, action, pid} when is_pid(pid) and is_function(action) -> 203 | new_value = action.(value) 204 | send(pid, {:ok, new_value}) 205 | run(new_value) 206 | _ -> 207 | run(value) 208 | end 209 | ``` 210 | 211 | При `get_and_update` съобщение очакваме втората стойност на съобщението да е функция. 212 | Извикваме я с текущата стойност за да получим нова стойност. Пример: 213 | 214 | ```elixir 215 | pid = Wrapper.start_link(5) 216 | send(pid, {:get_and_update, fn (v) -> v + 2 end, self()}) 217 | 218 | receive do 219 | msg -> IO.inspect(msg) 220 | end 221 | # {:ok, 7} 222 | ``` 223 | 224 | Лесно е да добавим и синхронен вариант: 225 | 226 | ```elixir 227 | def get_and_update(pid, action, timeout \\ 5000) when is_function(action) do 228 | send(pid, {:get_and_update, action, self()}) 229 | 230 | receive_value(timeout) 231 | end 232 | ``` 233 | 234 | Тук `receive_value` съдържа `receive` блокът, който е общ за всички синхронни 235 | функции. 236 | 237 | ## Прекратяване на `Wrapper` процес 238 | 239 | Това е лесно. Ще направим функция `Wrapper.stop/1`, която по `pid`, изпраща 240 | съобщение за спиране, при което рекурсията на `Wrapper` процеса спира: 241 | 242 | ```elixir 243 | receive do 244 | {:get, pid} when is_pid(pid) -> 245 | send(pid, {:ok, value}) 246 | run(value) 247 | {:set, new_value, pid} when is_pid(pid) -> 248 | send(pid, {:ok, new_value}) 249 | run(new_value) 250 | {:get_and_update, action, pid} when is_pid(pid) and is_function(action) -> 251 | new_value = action.(value) 252 | send(pid, {:ok, new_value}) 253 | run(new_value) 254 | {:stop, pid} when is_pid(pid) -> 255 | send(pid, :ok) 256 | _ -> 257 | run(value) 258 | end 259 | 260 | def stop(pid) do 261 | send(pid, {:stop, self()}) 262 | 263 | receive do :ok -> :ok; end 264 | end 265 | ``` 266 | 267 | Сега можем да проверим това поведение: 268 | 269 | ```elixir 270 | pid = Wrapper.start_link(5) 271 | Wrapper.stop(pid) 272 | Process.alive?(pid) 273 | # false 274 | ``` 275 | 276 | Модулът който направихме доста прилича на клас от обектно-ориентираното програмиране. 277 | Процесът му е инстанция и `PID`-ът я представлява. Държи състояние и функциите които дефинирахме 278 | на модула могат да се ползват като методи за достъп и промяна на това състояние. 279 | 280 | Интересното тук е, че това състояние не може да бъде споделяно, винаги получаваме негово копие 281 | в текущия процес. 282 | 283 | Това което имплементирахме си идва с `Elixir` и се нарича `Agent`. 284 | Всъщност нашият `Wrapper` е доста бледа и не-толкова-функционална версия на `Agent`. 285 | 286 | ## Agent 287 | 288 | Агентът е проста обвивка около състояние, съхранено в процес. Точно като нашия 289 | `Wrapper`. Разбира се, съхранението на състоянието е имплементирано чрез безкрайна рекурсия. 290 | 291 | Да видим как се създава един такъв `Agent` процес: 292 | 293 | ```elixir 294 | {:ok, pid} = Agent.start_link(fn -> 5 end) 295 | ``` 296 | 297 | При агентите задаваме състоянието си като резултат от анонимна функция. 298 | Всъщност почти всяка функция от `Agent` взима анонимна функция като аргумент: 299 | 300 | ```elixir 301 | Agent.get(pid, fn v -> v end) 302 | # 5 303 | ``` 304 | 305 | Идеята да подаваме функции на Агента е следната: Кодът в тази функция се изпълнява 306 | в процеса на Агента, което значи, че можем да си направим проста трансформация с 307 | оригиналната стойност и към текущият процес да се копира само резултата. 308 | Ние решаваме къде да се изпълни логиката. 309 | 310 | ```elixir 311 | Agent.get(pid, fn v -> v + 1 end) 312 | # 6 313 | ``` 314 | 315 | Като цяло Агентът е абстракция и може да бъде 'опакован' в подобен на `Wrapper` модул, 316 | който скрива разни детайли като подаването на тези функции. 317 | 318 | Агентите могат да се стартират като връзки, а могат да се стартират и като несвързани процеси, 319 | имат си функции за четене, промяна, промяна с ползване на текуща стойност и за асинхронна комуникация. 320 | Повече за тях можете да научите от [документацията](https://hexdocs.pm/elixir/Agent.html). 321 | 322 | ## Заключение 323 | 324 | Видяхме как можем да използваме процесите в `Elixir` като сървъри със състояние. 325 | Ще навлезем още по-навътре в тази тема, когато започнем да се занимаваме с `OTP`. 326 | Преди това, обаче, ще направим една крачка назад и ще разгледаме `type-spec` синтаксиса в `Elixir`, 327 | инструментът `dialyzer` и какво е _поведение_ (`behaviour`). 328 | -------------------------------------------------------------------------------- /posts/archive/tasks.md: -------------------------------------------------------------------------------- 1 | --- 2 | title_image_path: tasks.png 3 | created_at: 2017-05-18T00:33:36 4 | category: Програма 5 | tags: 6 | - elixir 7 | - process 8 | - OTP 9 | - Supervisor 10 | - GenServer 11 | - Application 12 | - Task 13 | - Task.Supervisor 14 | - await 15 | - async 16 | - async_nolink 17 | - simple_one_for_one 18 | --- 19 | 20 | # Процеси и OTP : Задачи 21 | 22 | Последната тема (засега) за процеси. Задачите са процеси, които правят главно 23 | едно действие и не комуникират с други процеси, освен когато свършат работата си. 24 | Тогава пращат съобщение на този процес, който е причинил тяхното създаване. 25 | 26 | Основната им цел е да превърнат прост `sequential` код в паралелен. 27 | 28 | ## Пример : паралелен Map версия 2 29 | 30 | ```elixir 31 | defmodule PEnum do 32 | def map(enumerable, func) do 33 | enumerable 34 | |> Enum.map(&(Task.async(fn -> func.(&1) end))) 35 | |> Enum.map(&Task.await/1) 36 | end 37 | end 38 | ``` 39 | 40 | Модулът `Task` има две важни функции : 41 | 1. `Task.async/1` (има и `MFA` версия - `Task.async/3`), която създава задача и я `link`-ва към текущия процес, като също така добавя монитор. 42 | 2. `Task.await/2`, която взима задача и опционално `timeout` (по подразбиране 5000 - или пет секунди) и чака резултат от задачата. 43 | 44 | С помощта на `Task` можем да пишем прости `background` задачи по лесен начин. 45 | 46 | В примера за всеки елемент на подадената колекция извикваме подадената функция в процес-задача и чакаме по ред на създаване на задачите. 47 | 48 | ## Структурата Task 49 | 50 | Една задача представлява структура с три полета. 51 | 52 | ```elixir 53 | Task.async(fn -> :nothing end) 54 | # %Task{owner: #PID<0.2724.0>, pid: #PID<0.2727.0>, ref: #Reference<0.0.3.551>} 55 | ``` 56 | 57 | Полетата представляват: 58 | * `owner` - Процесът, който ще получи съобщение, когато задачата приключи. 59 | * `pid` - `pid`-ът на процеса на задачата. 60 | * `ref` - Идва от мониторът прикрепен към задачата. 61 | 62 | Функцията `Task.await/2` извиква един `receive` и използва тези три полета да изчака отговор. 63 | 64 | ## Защо задачата е с link към процеса, който я създава? 65 | 66 | Защото ако той 'умре', задачата няма къде да върне резултата си, затова трябва да бъде изчистена. 67 | Ако пък задачата 'умре' значи нещо не е наред. Тя трябва да прави нещо наистина просто и не би трябвало да свърши живота си с причина, различна от `:normal`. 68 | 69 | Задача може да бъде стартирана със `Task.start/1` или `Task.start/3`, тогава няма да бъде `link`-ната към текущия процес, но и няма да изпрати резултат от изпълнението си. 70 | 71 | За повече информация и други функции свързани с `Task`, вижте [документацията](https://hexdocs.pm/elixir/Task.html#content). 72 | 73 | ## Task.Supervisor 74 | 75 | Понякога искаме задачите да изпращат резултат, но да не се `link`-ват към текущия процес. 76 | Да речем това са задачи, които си говорят с отдалечен компонент/`service` и биха могли да получат грешка отвън. 77 | 78 | Бихме могли да стартираме специален `simple_one_for_one Supervisor` като част от нашия `Application`, който да отговаря за тези задачи. 79 | Този `Supervisor` ще създава задачи, които могат и да излязат с грешка, но няма да убият процеса, който ги използва. 80 | Даже ще връщат резултат ако са създадени с правилната функция. 81 | 82 | Ако си спомняте, в `Blogit` основният `Supervisor` имаше следните спецификации: 83 | 84 | ```elixir 85 | children = [ 86 | supervisor(Blogit.Components.Supervisor, []), 87 | supervisor(Task.Supervisor, [[name: :tasks_supervisor]]), 88 | worker(Blogit.Server, [repository_provider]) 89 | ] 90 | ``` 91 | 92 | Точно този `:tasks_supervisor` се използва за създаване на задачи, които проверяват за промени в `git repository`-то зададено на `Blogit`. 93 | Ако то не е достъпно поради някаква причина, те ще излязат с грешка, но това няма да убие `Blogit.Server` процеса, който ги използва. 94 | 95 | Всъщност `Blogit.Server` процесът си създава по една такава задача на даден период от време така: 96 | 97 | ```elixir 98 | Task.Supervisor.async_nolink( 99 | :tasks_supervisor, Blogit.Logic.Updater, :check_updates, [state] 100 | ) 101 | ``` 102 | 103 | По този начин с `async_nolink`, нямаме `link`, но имаме монитор, затова процесът, който го извиква ще получи съобщението си. 104 | 105 | В `Task.Supervisor` има други функции за създаване на задачи с и без връзка, за повече информация вижте [документацията](https://hexdocs.pm/elixir/Task.Supervisor.html#content). 106 | 107 | ## GenServer и Task 108 | 109 | Интересна е ситуацията, когато създадем задача в `GenServer` (или нещо базирано или подобно на него) с `Task.Supervisor.async_nolink`. 110 | Ако не използваме `Task.await/2`, можем да си дефинираме `handle_info callback`, който ще бъде извикан с резултата от задачата във формата `{ref, result}`. 111 | 112 | Това може да се използва от `GenServer` процеси, които обслужват клиентски заявки и не трябва да губят време с код, който би могъл да отнеме време. 113 | Кодът в процес е `sequential` затова ако клиентски процес направи заявка към такъв `GenServer` процес, а той се занимава с тежко изчисление, например, ще имаме `timeout`. 114 | Ако обаче такива тежки изчисления се правят в задачи, които по някое време ни ги върнат чрез `handle_info callback`, променяйки състоянието на `GenServer` процеса, той ще остане `responsive`. 115 | Тази стратегия е подходяща и за `request`-и към отдалечени ресурси. 116 | 117 | Именно затова я ползваме и в `Blogit`. Проверката за промени с `git` се прави с `git fetch`, което обикновено е заявка към отдалечен сървър. Не трябва код, който управлява компонентите, използвани от потребителски процеси 118 | да зависи от заявки към отдалечени ресурси. 119 | 120 | Важното в подобни случаи е да дефинираме и още един, допълнителен `handle_info callback`: 121 | 122 | ```elixir 123 | def handle_info({:DOWN, _ref, :process, _pid, _status}, state) 124 | ``` 125 | 126 | Това е важно, защото както и да завърши изпълнение всяка задача ще изпрати подобно съобщение 127 | на `GenServer`-а, все пак той я наблюдава с монитор. 128 | Ако не дефинираме такъв `callback` ще има грешка че не обработваме съобщение, което ни е изпратено, което би убило процеса. 129 | 130 | ## Заключение 131 | 132 | Тази кратка статия бележи края на поредицата свързана с `OTP` поведенията и модулите около тях. 133 | Има още. Следващите статии, обаче, са на тема мета-програмиране. 134 | След тях е възможно да се върнем към `OTP` и неговите бази данни, поведения и инструменти. 135 | -------------------------------------------------------------------------------- /posts/en/why_elixir.md: -------------------------------------------------------------------------------- 1 | --- 2 | title_image_path: why_elixir.jpg 3 | created_at: 2019-03-14T16:19:23 4 | category: Програма 5 | author: Ivan, Jordan 6 | tags: 7 | - elixir 8 | - introduction 9 | - erlang 10 | - beam 11 | - functional programming 12 | --- 13 | 14 | # Why Learn And Use Elixir? 15 | 16 | This blog post is meant to accompany the first lecture[1](http://gitpitch.com/ElixirCourse/presentations-2019/?p=welcome) of our course _Functional Programming with Elixir_, 17 | that takes place in FMI (Faculty Of Mathematcis And Informathics of The Sofia University) during the summer semester of 2018/2019. 18 | We will discuss why one would want to learn and use Elixir. 19 | But first, a bit of history: 20 | 21 | ### Erlang 22 | 23 | Erlang is a functional language, developed in the mid-80s by Ericsson with the intent to build telecom software. 24 | Although, the motivations behind its creation were primarily driven by the field's specific requirements, Erlang can be applied to many other needs. 25 | One of least cited reasons[2](https://www.erlang-factory.com/upload/presentations/416/MikeWilliams.pdf) behind its development was the need to replace the highly specialized hardware and software used at the time (including the operating system). 26 | Erlang does not explicitly support the programming requirements of a telephone or otherwise telecom venture, nor does it pose any specialized hardware or OS requirements. 27 | Instead, the language offers solutions to the technical needs and problems encountered while developing this type of software: 28 | 29 | - concurrency; 30 | - scalability; 31 | - distributability; 32 | - error tolerance; 33 | - updating without service interruption; 34 | - quick query response even when under high stress; 35 | 36 | All of this happened at a time, when internet was not yet widespread and most programs worked on a single computer with no connection. 37 | This type of software does not require any distributability or horizontal scalability. 38 | At the time, personal computers did not have 2-, 4-, or 16-core processors – the need for the software to automatically accelerate with addition of more processors/cores was not yet present. This directly impacted the design and purpose of the programming languages developed at this time or those earlier ones they were based on. 39 | 40 | Even if you never use Erlang, its ideas and approach[3](https://ferd.ca/the-zen-of-erlang.html) to solving a specific type of problems is easily applicable when using other languages. 41 | 42 | ### Where does Erlang stand today? 43 | 44 | All of the above sounds good, but how many of us write telecom software or, in fact, any software with such requirements? 45 | Well, a lot of us, actually. 46 | The requirements of today's web applications overlap completely with the concerns addressed by Erlang's creators at the time of its development. 47 | Having a language and a platform based on meeting those specifics without the need to load external libraries to implement the missing functionality of the language is a huge advantage. 48 | 49 | ![Ad-hoc Erlang implementation](https://raw.githubusercontent.com/ElixirCourse/blog/master/assets/erlang_ad_hoc_implement.png) 50 | 51 | If you want to know more about the guarantees provided by BEAM[4](http://www.erlang-factory.com/upload/presentations/708/HitchhikersTouroftheBEAM.pdf) – the virtual machine in which Erlang runs, you would do best to watch [this video](https://www.youtube.com/watch?v=5SbWapbXhKo). 52 | 53 | ### Elixir 54 | 55 | Why do talk so much about Erlang in this article named "Why Learn And Use Elixir?" 56 | ![Best part of Elixir tweet](https://raw.githubusercontent.com/ElixirCourse/blog/master/assets/elixir_most_important_part.png) 57 | 58 | 59 | 60 | Elixir is a functional language. Its development started in 2011, with version 1.0 going live in 2014. 61 | 62 | Elixir runs on Beam. 63 | It can use everything that was written and everything that could be written in Erlang. 64 | This allows us to combine a new and modern language with libraries that stood the test of time. 65 | Elixir's Erlang basis does not come with any additional hassle or the need to use cryptic syntax or type transformations. 66 | Let's compare the different ways Elixir and Erlang call a function from a given module: 67 | 68 | Elixir: 69 | 70 | ```elixir 71 | DateTime.utc_now() 72 | ``` 73 | 74 | Erlang: 75 | 76 | ```erlang 77 | :crypto.strong_rand_bytes(64) 78 | ``` 79 | 80 | Elixir modules always start with an upper case, while Erlang modules start with : and a lower case. 81 | 82 | Let's look at just some of the things WE like about Elixir/Erlang: 83 | 84 | - Functional Language – immutable and persistent data structures, pattern matching, higher order functions, function compositions 85 | - Balance between pure and side-effect functions. At this point, some of the sworn Haskell fans might disagree. But some things have to sacrificed at the altar of productivity. 86 | - Language-level processes – it may sound dull, but it's the basis for Elixir's famous concurrency and distributability 87 | - OTP (Open Telecom Platform) – this is a platform that is no longer used solely for telecom solutions, but also for software that has similar requirements, which, as mentioned earlier in the article, a lot of today's web applications do. During this course, we will explore some of the libraries that OTP provides. For now, you can look at additional information [here](https://learnyousomeerlang.com/what-is-otp#its-the-open-telecom-platform). 88 | 89 | It's wrong to think of Elixir as a competitor that will tank Erlang. On the contrary, Elixir is one of the best things to happen to it. Erlang, as the basis, has seen massive development due to the interest in Elixir and the influx of new people to the community. 90 | 91 | But since Elixir emerged and we're running a course about it and not Erlang, it must bring something new to the table: 92 | 93 | - Elixir is Erlang. Everything advantage of Erlang is an advantage of Elixir. 94 | - Elixir is more. 95 | - Better tools than Erlang. 96 | - Good documentation. The language's creator claims that documentation errors should be handled as a program error. 97 | - There are experienced, responsive and intelligent people behind the language and its ecosystem. 98 | - Convenient and visually pleasing syntax. Erlang has a minimalistic syntax, even too much so. This could sometimes lead to boilerplate and repetitive code. The lack of macros exacerbates this problem. 99 | - Powerful macros. As opposed to the ones in C/C++, macros in Elixir do not work with strings, but with AST (Abstract Syntax Tree). Macros in Elixir are mainly influenced by those in Lisp and Clojure. 100 | - Polymorphism through protocols. 101 | - Proper libraries. A good number of libraries have been written in Erlang and have been used long enough to make a safe claim that they're stable and well tested. But there are also a good number of libraries written relatively recently in Elixir. This gives the advantage of avoiding errors present in older libraries of other languages. Good examples would be [Plug](https://hexdocs.pm/plug/readme.html), [Ecto](https://hexdocs.pm/ecto/Ecto.html), [Phoenix](http://phoenixframework.org), [GenStage](https://github.com/elixir-lang/gen_stage) and many others. 102 | - Pipe operator `|>`. With its help, code is unquestionably more pleasing, convenient, readable, as well as easy to change. This provides the not so obvious advantage of consistency in the standard library of the language. In order to make using `|>` easier, functions take the data they're working on as their first argument. 103 | 104 | Let's look at an example of the difference between a code that uses `|>` and code that does not. 105 | 106 | The goal is to execute a number of transformations on a list of numbers. 107 | 108 | One way to write this out is: 109 | 110 | ```elixir 111 | data = [1,2,3,4,5] 112 | data_squared = Enum.map(data, fn n -> n*n end) 113 | data_filtered = Enum.filter(data_squared, fn n -> n >10 end) 114 | sum = Enum.reduce(data_filtered, 1, &(&1+&2)) 115 | ``` 116 | 117 | What if we don't want to use so many temporary variables? We can implement the functions: 118 | 119 | ```elixir 120 | Enum.reduce(Enum.filter(Enum.map([1,2,3,4,5], fn n -> n*n end),fn n ->n > 10 end), 1, &(&1 + &2)) 121 | ``` 122 | 123 | In this example, readability is near-zero and adding further transformations is hardly convenient. 124 | 125 | This is where `|>` comes in handy. It presents the result on its left side as a first argument in the expression on its right. Nothing more, nothing less, but just enough to allow us to write code in the following manner: 126 | 127 | ```elixir 128 | result = 129 | [1, 2, 3, 4, 5] 130 | |> Enum.map(fn n -> n * n end) 131 | |> Enum.filter(fn n -> n > 10 end) 132 | |> Enum.reduce(1, &(&1 + &2)) 133 | ``` 134 | 135 | ### What about the downsides of Erlang/Elixir? 136 | 137 | Just so we're not blindly spewing superlatives about the language, it would be appropriate if we mention some of its disadvantages. Some of them are in the below list, just because they're unseemly at first glance, but do carry value that starts to show over time and we will explore why. 138 | 139 | - Lack of libraries found in many other languages. Since Elixir is a fairly new language, and Erlang does not benefit from the popularity of languages like Java, Python or Ruby, sometimes you won't be able to locate something you intuitively expect to find. Sometimes, you'll only be able to find an Erlang library that could do the job, but this will require learning some Erlang. 140 | - The delusion, brought on by people not truly familiar with the language. A common complaint is that the syntax concerning concurrency is not as simple and minimalistic as in Go. The reason for this is that Elixir's concurrency is meant to solve problems related to high availability, while in Go it is simply used for concurrency's sake. 141 | - At first glance, it's not obvious how to locate certain functions. If you want to return the length of a list, would you find the function in the `List` module? You'll actually discover there is no `List.size`, nor `List.length`. The length function is called with `Enum.count`, which runs `:erlang.length` (accessible as `Kernel.length` or simply `length`) under the hood. However, using `Enum.count` we can find the number of elements of everything the `Enumerable` protocol implements. 142 | 143 | The library problem is one that every language suffers at this stage of its development. To clarify, we are not talking about important libraries like HTTP server/client, database, web framework, JSON decoders, etc; libraries like that are available, and they're quite good, too. We're talking about small things that aren't really a problem of the language or the platform and is fixable, albeit slowly over time. During this FMI Elixir course we will take into account contributions to open source libraries and credit will be given accordingly. Even more so for newly created libraries. 144 | 145 | ### Sources: 146 | 147 | [1] Tsvetinov, Nikolay (Meddle). Functional Programming With Elixir. Gitpich, 18.02.2019. Available from: https://gitpitch.com/ElixirCourse/presentations-2019/?p=welcome [cited 22.02.2019]. 148 | 149 | [2] Williams, Mike. The True story about why we invented Erlang and A few things you don’t want to tell your Manager. Erlang Factory, 26.02.2011. Available from: https://www.erlang-factory.com/upload/presentations/416/MikeWilliams.pdf [cited 22.02.2019]. 150 | 151 | [3] Hebert, Fred. The zen of Erlang. Ferd, 08.02.2016. Available from: https://ferd.ca/the-zen-of-erlang.html [cited 22.02.2019]. 152 | 153 | [4] Virding, Robert. Hitchhiker’s Tour of the BEAM. Erlang Factory, 2012. Available from: http://www.erlang-factory.com/upload/presentations/708/HitchhikersTouroftheBEAM.pdf [cited 22.02.2019]. 154 | 155 | Jurić, Saša. ElixirDaze 2017- Solid Ground by Saša Juric. YouTube, 16.03.2017. Availalble from: https://www.youtube.com/watch?v=5SbWapbXhKo [cited 22.02.2019]. 156 | 157 | [Official site of Erlang](http://www.erlang.org/) 158 | 159 | [Official site of Elixir](https://elixir-lang.org/) 160 | -------------------------------------------------------------------------------- /posts/materials/binaries.md: -------------------------------------------------------------------------------- 1 | --- 2 | title_image_path: binaries.jpg 3 | category: Програма 4 | created_at: 2019-03-07T17:00:00 5 | tags: 6 | - elixir 7 | - binaries 8 | - erlang 9 | - pattern matching 10 | - strings 11 | --- 12 | 13 | # Двоични структури (Binaries) 14 | 15 | В тази публикация ще се запознаем по-задълбочено с двоичните структури в _Elixir_, каква е вътрешната им репрезентация, как и за какво са ни полезни. 16 | 17 | ## Конструкция 18 | 19 | Една _binary_ структура представлява поредица от битове (често поредица от байтове). 20 | Дефинира се със следната конструкция: 21 | 22 | ```elixir 23 | <<>> 24 | #=> <<>> 25 | ``` 26 | 27 | В тази конструкция слагаме поредица от числа представляващи битове или байтове: 28 | 29 | ```elixir 30 | << 123, 23, 1 >> 31 | #=> << 123, 23, 1 >> 32 | ``` 33 | 34 | Всяко число представлява един байт. В този ред на мисли, това е вярно: 35 | 36 | ```elixir 37 | << 0b01111011, 0b00010111, 0b00000001 >> == << 123, 23, 1 >> 38 | #=> true 39 | ``` 40 | 41 | Числата винаги представляват един байт. Ако числото е по-голямо от **255**, то е "отрязано" до един байт. 42 | 43 | ```elixir 44 | << 280 >> 45 | #=> <<24>> 46 | ``` 47 | 48 | Разбира се има начин числата да представляват повече от един байт. Това става с помощта на модификатора `size`. 49 | 50 | ```elixir 51 | << 280::size(16) >> 52 | #=> << 1, 24 >> 53 | 54 | # или 55 | 56 | << 280::16 >> 57 | #=> << 1, 24 >> 58 | 59 | << 0b00000001, 0b00011000 >> == << 280::16 >> 60 | #=> true 61 | ``` 62 | 63 | С този модификатор можем да представим число в `7` бита: 64 | 65 | ```elixir 66 | << 129::7 >> 67 | #=> <<1::size(7)>> # Всъщност е отрязано от 10000001 - взимат се десните 7 бита - 0000001 68 | ``` 69 | 70 | Интересно е съхранението на числа с плаваща запетая като _binaries_. 71 | Винаги се представят като двоична структура от **8** байта: 72 | 73 | ```elixir 74 | << 5.5::float >> 75 | #=> <<64, 22, 0, 0, 0, 0, 0, 0>> 76 | ``` 77 | 78 | Това е така, защото числата с плаваща запетая в _Elixir_ са с _double_ прецизност или **64** битови. 79 | 80 | ```elixir 81 | <> = <<5.5::float>> 82 | #=> <<64, 22, 0, 0, 0, 0, 0, 0>> 83 | {sign, exponent, mantissa} 84 | #=> {0, 1025, 1688849860263936} 85 | ``` 86 | 87 | ## Функции и операции свързани с binaries 88 | 89 | ### Конкатенация 90 | 91 | Възможно е да конкатенираме _binary_ структури с оператора `<>`. 92 | 93 | ```elixir 94 | << 83, 79, 83 >> <> << 24, 4 >> 95 | #=> <<83, 79, 83, 24, 4>> 96 | ``` 97 | 98 | Друг начин за конкатенация е: 99 | 100 | ```elixir 101 | << 83, 79, 83, << 24, 4 >> >> 102 | #=> <<83, 79, 83, 24, 4>> 103 | ``` 104 | 105 | Този код е аналогичен на примера с оператора `<>`. 106 | 107 | ### Размер 108 | 109 | В паметта, за _binary_ структурите, има мета-информация, която съхранява дължината им. 110 | Това значи, че пресмятането на дължината на _binary_ структура е операция с константа сложност. 111 | Поради това и функцията за намиране на дължината на двоична структура има в името си `size`: 112 | 113 | ```elixir 114 | byte_size(<< 83, 79, 83 >>) 115 | #=> 3 116 | ``` 117 | 118 | Когато боравим с битове, чийто брой не е кратен на **8**, ще се закръгли нагоре: 119 | 120 | ```elixir 121 | byte_size(<< 34::5, 23::2, 12::2 >>) 122 | #=> 2 123 | 124 | # Между другото: 125 | << 34::5, 23::2, 12::2 >> == << 22, 0::1 >> 126 | #=> true 127 | ``` 128 | 129 | Разбира се можем да видим и точната дължина в битове, което е отново **O(1)** операция: 130 | 131 | ```elixir 132 | bit_size(<< 34::5, 23::2, 12::2 >>) 133 | #=> 9 134 | ``` 135 | 136 | ### Проверки 137 | 138 | Има два начина за проверяване дали типа на дадена променлива е поредица от битове. 139 | 140 | 1. `Kernel.is_bitstring/1` - Истина за всяка валидна _binary_ структура. 141 | 2. `Kernel.is_binary/1` - Истина е само ако `bit_size/1` връща число кратно на **8** - тоест структурата е поредица от байтове. 142 | 143 | В повечето случаи ще използваме `Kernel.is_binary/1`. 144 | 145 | ### Sub-binaries 146 | 147 | С функцията `Kernel.binary_part/3` можем да боравим с части от дадена _binary_ стойност: 148 | 149 | ```elixir 150 | binary_part(<< 83, 79, 83, 23, 21, 12 >>, 1, 3) 151 | #=> <<79, 83, 23>> 152 | ``` 153 | 154 | Взима под-структура от индекс - втория аргумент с брой елементи - третия. Очаквайте 155 | `ArgumentError`, ако тези аргументи са невалидни индекс и/или дължина за дадената структура. 156 | 157 | Важното тук е, че това е бърза операция - нищо не се копира, просто получавате указател 158 | от дадено място в паметта, с дадена дължина. 159 | 160 | ## Pattern matching 161 | 162 | В _Elixir_ можем да съпоставяме и _binaries_: 163 | 164 | ```elixir 165 | << x, y, x >> = << 83, 79, 83 >> 166 | #=> "SOS" 167 | 168 | x 169 | #=> 83 170 | 171 | y 172 | #=> 79 173 | ``` 174 | 175 | Друга интересна възможност е да съпоставим променлива към повече от един байт. 176 | 177 | ```elixir 178 | << x, y, z::binary >> = << 83, 79, 83, 43, 156 >> 179 | #=> <<83, 79, 83, 43, 156>> 180 | z 181 | #=> << 83, 43, 156 >> 182 | 183 | # Ако не добавим `::binary` автоматично съпоставя променлива на 1 байт: 184 | << x, y, z >> = << 83, 79, 83, 43, 156 >> 185 | #=> ** (MatchError) no match of right hand side value: <<83, 79, 83, 43, 156>> 186 | ``` 187 | 188 | Ето пример за разделяне и изваждане на частите на число с плаваща запетая, 189 | така както е [представено](https://en.wikipedia.org/wiki/Double-precision_floating-point_format): 190 | 191 | ```elixir 192 | << sign::size(1), exponent::size(11), mantissa::size(52) >> = << 4815.162342::float >> 193 | #=> <<64, 178, 207, 41, 143, 62, 204, 196>> 194 | 195 | sign 196 | #=> 0 197 | ``` 198 | 199 | Тук знакът е **1** при отрицателно число и **0** при положително - в случая е **0**. 200 | Пази се в един бит. 201 | 202 | Цялото число може да се пресметне с **(-1)sign(1 + mantissa/252)2exponent - 1023**: 203 | 204 | ```elixir 205 | :math.pow(-1, sign) * (1 + mantissa / :math.pow(2, 52)) * :math.pow(2, exponent - 1023) 206 | #=> 4815.162342 207 | ``` 208 | 209 | Освен `float` и `binary` модификаторите, има `integer` модификатор, който се прилага 210 | автоматично ако никой друг не е използван. 211 | Модификаторът `bytes` е аналогичен на `binary`. 212 | Модификаторите `bits` и `bitstrig` се използват за съпоставяне на битстринг съдържание - поредица 213 | от битове с неопределена дължина. 214 | 215 | ```elixir 216 | << x::5, y::bits >> = << 225 >> 217 | #=> <<225>> 218 | 219 | x 220 | #=> 28 221 | 222 | y 223 | #=> <<1::size(3)>> 224 | ``` 225 | 226 | Модификаторите `bitstrig` и `binary` без дължина могат да се използват само в края на _binary_ структурата. 227 | 228 | Останалите възможни модификатори са `utf8`, `utf16` и `utf32`, които са свързани с _unicode_. 229 | Ще се върнем към `utf8`, когато си говорим за низове в следващата публикация. 230 | 231 | Интересно нещо, свързано с модификаторите, е, че `size` работи с тях. 232 | `size` задава дължина в битове, когато работим с `integer`, но ако работим 233 | с `binary` модификатор, `size` е в байтове: 234 | 235 | ```elixir 236 | << x::binary-size(4), _::binary >> = << 83, 222, 0, 345, 143, 87 >> 237 | #=> <<83, 222, 0, 89, 143, 87>> 238 | 239 | x # 4 байта 240 | #=> <<83, 222, 0, 89>> 241 | ``` 242 | 243 | ## Имплементация 244 | 245 | Всеки процес в _Elixir_ има собствен _heap_. 246 | В тази памет се съхраняват различни структури от данни и стойности. 247 | Когато два процеса си комуникират, съобщенията, които се изпращат между тях, се копират в _heap_-овете им. 248 | 249 | Когато си говорим за _binaries_, обаче, има една голяма разлика с този модел. 250 | Ако _binary_-то е **64 байта** или по-малко, то се съхранява в _heap_-a на процеса си и се копира при размяна с друг процес, както е описано по-горе. 251 | Такива _binary_ структури наричаме **heap binaries**. 252 | 253 | Когато структурата ни е по-голяма от **64 байта**, тя не се пази в _process heap_-a. Пази се в обща памет за всички процеси на даден _node_ (Erlang виртуална машина). В _process heap_-a се пази малък обект, наречен **ProcBin**, който е указател към даденото _binary_, съхранено в общия _heap_. В този общ _heap_, _binary_ структура може да бъде сочена от множество такива _ProcBin_ указатели от множество процеси. Има _reference counter_ за всеки от тези указатели. Когато той стане **0**, _Garbage Collector_-ът ще може да изчисти _binary_-то от общия _heap_. 254 | Такива _binary_ структури наричаме **refc binaries**. 255 | 256 | Защо това е хубаво? Защото при по-големи двоични структури няма постоянно копиране 257 | между процесите и съществуват различни оптимизации. 258 | Разбира се, трябва да се внимава с тези _refc binaries_. 259 | 260 | Говорейки си за имплементацията е хубаво да обърнем внимание на два вида специални указатели, свързани с двоичните структури. 261 | 262 | По-горе споменахме _sub binary_ стойностите, получен чрез `Kernel.binary_part/3`, която вътрешно ползва `:erlang.split_binary/2`. 263 | Това е специален указател който сочи към част от дадено _binary_. Няма копиране (защото всичко е _immutable_). Създаването на _sub binary_ е евтина операция, но ако става въпрос за _refc binary_, _reference counter_-a се увеличава. 264 | 265 | Друг специален обект е _match context_-ът. Той е подобен на _sub binary_, но оптимизиран за*binary pattern matching*. Държи указател към двоичните данни в паметта, и когато нещо е _match_-нато, указателят се придвижва напред. Компилаторът отлага създаването на _sub binary_ за всяка _match_-ната променлива, ако е възможно, и преизползва един и същ _match context_. 266 | 267 | ### Оптимизации при конкатенация 268 | 269 | Да видим как работи конкатенацията, когато сме запознати с модела на пазене на двоичните структури в паметта. 270 | Имаме следния код: 271 | 272 | ```elixir 273 | x = << 83, 222, 0, 89 >> 274 | y = << x, 225, 21 >> 275 | z = << y, 125, 156 >> 276 | a = << y, 15, 16, 19 >> 277 | ``` 278 | 279 | Нека обясним всеки ред в този пример. 280 | 281 | На първия ред виждаме, че `x` сочи към ново _binary_, което е **4** байта, следователно се създава в _heap_-a на текущия процес. 282 | 283 | Вторият ред представлява конкатенация. 284 | Към `x` се добавят още **2** байта и това ново _binary_ се присвоява на `y`. 285 | Какво става с паметта? 286 | Операцията за добавяне всъщност създава ново _refc binary_. Така е имплементирана. В общата памет се заделя място с големина `max(2 * byte_size(x), 256)`, в случая - **256** байта: 287 | 288 | ``` 289 | [4] -> |83|222|0|89| | | | |...| -> 256 байта 290 | ``` 291 | 292 | Този указател е създаден в _heap_-a на текущия процес, също _heap binary_-то за `x`, което беше създадено на първия ред, сега може да бъде изчистено от _Garbage Collector_-a на процеса си. Байтовете, които трябва да се добавят са добавени в свободното пространство: 293 | 294 | ``` 295 | [4] -> |83|222|0|89|225|21| | |...| -> 256 байта 296 | [6] -^ 297 | ``` 298 | 299 | Какво става на **3**-ти ред? 300 | Искаме да добавим към `y` още **2** байта и да присвоим на `z` резултата. Тъй като след байтовете на `y` има свободно място, те се преизползват за `z`. Няма копиране, защото всичко е _immutable_, от което следва че може да се преизползва всичко, когато е възможно: 301 | 302 | ``` 303 | [4] -> |83|222|0|89|225|21|125|156| | | |...| -> 256 байта 304 | [6] -^ 305 | [8] -^ 306 | ``` 307 | 308 | Сега става интересно. 309 | На **4**-ти ред искаме да добавим към `y` **3** байта и да присвоим резултата към `a`. Сега след стойността на `y`, в паметта има данни и не можем да преизползваме пространството. Затова _run-time_ системата, копира стойността на `y` на ново място в паметта (използва формулата по-горе) и добавя _3_-те нови байта там: 310 | 311 | ``` 312 | [4] -> |83|222|0|89|225|21|125|156| | | |...| -> 256 байта 313 | [6] -^ 314 | [8] -^ 315 | 316 | [9] -> |83|222|0|89|225|21|15|16|19| | |...| -> 256 байта 317 | ``` 318 | 319 | Тази оптимизация е възможна в доста малко случаи. Има операции, които карат паметта да стане компактна и да освободи неизползваните байтове, от което следва че подобна оптимизация при добавяне ще е невъзможна. Такива операции са пращането на _binary_-то като съобщение между процеси, както и създаването на _match context_ при _pattern matching_. 320 | 321 | С този последен пример завършваме тази публикация. 322 | Показахме ви _binary_ структурите в по-голяма дълбочина преди да говорим за низове. Почти всичко казано не е специфично за _Elixir_ и идва от _Erlang_. Следващата статия ще разгледа низовете в дълбочина - ще си говорим за _unicode_ и _utf8_ и ще споменем някои от функциите в `String` модула. 323 | -------------------------------------------------------------------------------- /posts/materials/how_to_git.md: -------------------------------------------------------------------------------- 1 | --- 2 | title_image_path: git_logo.png 3 | category: Програма 4 | created_at: 2023-02-24T17:00:00 5 | tags: 6 | - git 7 | - github 8 | - homework 9 | --- 10 | 11 | # Получаване и изпращане на домашни 12 | 13 | За домашните в курса *Функционално програмиране с Elixir* се използва [Github Classroom](https://classroom.github.com/). 14 | Използвайки тази платформа, за да получите и предадете домашно, трябва да направите следните стъпки: 15 | 16 | - Да имате профил в [Github](https://github.com). 17 | - Да посетите [сайта на курса](https://elixir-lang.bg/), където ще бъде публикуван линк към домашното. 18 | - Когато посетите този линк и натиснете бутона `Accept this assignment`, ще бъде създадено специално хранилище, до което достъп имате само Вие, в следната [Github организация](https://github.com/orgs/ElixirCourseHW/repositories). 19 | - В хранилището ще намерите условието и инструкциите за изпълнение на домашното. 20 | - Преди да изтече крайният срок, Вашето решение трябва да бъде добавено в хранилището. 21 | 22 | # Работа с Github 23 | 24 | ## Използвайки графичния интерфейс 25 | 26 | Най-лесният начин за работа с Github е с помощта на графичния интерфейс. 27 | Може да добавяте нови файлове с бутона `Add files`. 28 | Създадени файлове може да редактирате като ги отворите и натиснете бутона `Edit` (с иконка на молив/химикалка), направите желаните промени 29 | чрез предоставения редактор и натиснете бутона `Commit changes`. 30 | 31 | **Не** ви препоръчваме да използвате графичния интерфейс. Уменията за работа с git от командния ред са необходими умения за всеки програмист. 32 | 33 | ## Използвайки командния ред 34 | 35 | ### Github CLI 36 | 37 | Най-лесният начин за интеракция с Github от командния ред е с помощта на [Github CLI](https://cli.github.com/). 38 | След като сте го инсталирали Github CLI, може да преминете към автентикацията. 39 | 40 | #### Автентикация 41 | 42 | Отворете терминал и изпълнете следната команда: 43 | 44 | ```bash 45 | $ gh auth login 46 | ``` 47 | 48 | Ще видите следното: 49 | 50 | ```bash 51 | ? What account do you want to log into? [Use arrows to move, type to filter] 52 | > GitHub.com 53 | GitHub Enterprise Server 54 | ``` 55 | 56 | Изберете `Github.com`. 57 | След това ще бъдете помолени да изберете начин за автентикация: 58 | 59 | ```bash 60 | ? What is your preferred protocol for Git operations? [Use arrows to move, type to filter] 61 | > HTTPS 62 | SSH 63 | ``` 64 | 65 | Изберете `HTTPS`. На следващата стъпка ще бъдете помолени да се съгласите да използвате Github за удостоверяване. Съгласете се. 66 | 67 | ```bash 68 | Authenticate Git with your GitHub credentials? [Y/n] y 69 | ``` 70 | 71 | На следащата стъпка изберете автентикация чрез уеб браузър. Ще ви бъде показан код (например `ABCD-1234`), който трябва да въведете в браузъра. 72 | 73 | ```bash 74 | ? How would you like to authenticate GitHub CLI? [Use arrows to move, type to filter] 75 | > Login with a web browser 76 | Paste an authentication token 77 | ``` 78 | 79 | При правилно въвеждане на кода и следване на останалите стъпки в браузъра, във Вашия терминал трябва да видите следното: 80 | 81 | ```bash 82 | ✓ Authentication complete. 83 | - gh config set -h github.com git_protocol https 84 | ✓ Configured git protocol 85 | ✓ Logged in as IvanIvanoff 86 | ``` 87 | 88 | Това означава, че автентикацията е успешна. 89 | Ако желаете да смените протокола, може да изпълните: 90 | 91 | ```bash 92 | gh config set -h github.com git_protocol ssh 93 | ``` 94 | 95 | #### Работа с Github CLI 96 | 97 | След като сте настроили Github CLI, за да клониранете вашето хранилище може да изпълните: следната команда, използвайки Вашето хранилище: 98 | 99 | ```bash 100 | $ gh repo clone ElixirCourseHW/homework-1-year-2023-IvanIvanoff 101 | ``` 102 | 103 | Преди да започнем работа, трябва да създадем нов клон в нашето хранилище, което ще съдържа промените: 104 | 105 | ```bash 106 | $ cd homework-1-year-2023-IvanIvanoff 107 | $ git checkout -b my-solution 108 | ``` 109 | 110 | Нека да добавим файл `hello.txt` в хранилището: 111 | 112 | ```bash 113 | $ touch hello.txt 114 | $ echo "Hello, world!" > hello.txt 115 | ``` 116 | 117 | След това може да изпълним следната команда, за да съхраним файла в нашето локално хранилище: 118 | 119 | ```bash 120 | $ git add hello.txt 121 | $ git commit -m "Adding hello.txt" 122 | ``` 123 | 124 | За да качим промените в Github хранилището, може да изпълним следната команда: 125 | 126 | ```bash 127 | $ gh pr create 128 | ➜ homework-1-year-2023-IvanIvanoff git:(my-solution) gh pr create 129 | ? Where should we push the 'my-solution' branch? [Use arrows to move, type to filter] 130 | > ElixirCourseHW/homework-1-year-2023-IvanIvanoff 131 | Create a fork of ElixirCourseHW/homework-1-year-2023-IvanIvanoff 132 | Skip pushing the branch 133 | Cancel 134 | ``` 135 | 136 | Изберете първата възможност, въведете заглавие, не въвежайте допълнително описание (или въведете, ако искате) и натиснете `Enter`. 137 | 138 | ```bash 139 | Creating pull request for my-solution into main in ElixirCourseHW/homework-1-year-2023-IvanIvanoff 140 | 141 | ? Title my solution 142 | ? Body 143 | ? What's next? [Use arrows to move, type to filter] 144 | Submit 145 | Submit as draft 146 | > Continue in browser 147 | Add metadata 148 | Cancel 149 | ``` 150 | 151 | В показаното меню изберете `Continue in browser`, което ще отвори нова страница в браузъра, където можете да видите Вашите промени, да промените заглавието или описанието и да създадете Pull Request. 152 | 153 | След създаването на този Pull Request, може да натиснете бутона `Merge pull request` и вашите промени ще бъдат добавени към `main` клона. С извършването на тази стъпка се счита, че Вашето домашно е предадено успешно. Може да правите промени неограничен брой пъти преди изтичането на крайния срок. Когато крайният срок изтече, правата за писане в това хранилище Ви се отнемат автоматично. 154 | 155 | За да синхронизирате локалното хранилище с хранилището в Github, може да изпълните следната команда: 156 | 157 | ```bash 158 | $ gh repo sync 159 | ``` 160 | 161 | и ще видите следното: 162 | 163 | ```bash 164 | ✓ Synced the "main" branch from ElixirCourseHW/homework-1-year-2023-IvanIvanoff to local repository 165 | ``` 166 | 167 | За да започнете работа върху нова задача, върнете се обратно в `main` клона и създайте нов клон и изпълнете стъпките отново. Нужно е да го направите от `main` клона, за да започнете новата задача от състоянието, което съдържа последните промени. 168 | 169 | ```bash 170 | $ git checkout main 171 | $ git checkout -b fix-known-bugs-in-solution 172 | ``` 173 | 174 | Забележка: Това не е единственият начин за работа с `git` и `Github`, но е един от най-лесните. При добро познаване на `git`, работният процес може да протече по различен начин. 175 | 176 | ### SSH автентикация 177 | 178 | Настройването на SSH автентикация е една идея по-сложно от използването на Github CLI и няма да го описваме подробно тук. 179 | Инструкции може да намерите [тук](https://docs.github.com/en/authentication/connecting-to-github-with-ssh) -------------------------------------------------------------------------------- /posts/materials/maps.md: -------------------------------------------------------------------------------- 1 | --- 2 | title_image_path: keys.jpg 3 | category: Програма 4 | created_at: 2019-03-06T19:00:00 5 | tags: 6 | - elixir 7 | - map 8 | - pattern matching 9 | - dict 10 | - struct 11 | - module 12 | --- 13 | 14 | # Речници (Maps) 15 | 16 | В *Elixir* за асоциативен списък, речник или ключ-стойност структура могат да 17 | се използват няколко типа. 18 | Поради развитието на *Erlang* и на *Elixir* някои от тях отпаднаха като предпочитани опции и в момента се използват *Map*-ове. 19 | 20 | ## Малко история 21 | 22 | В *Erlang* до версия **17** е нямало *Map*-ове. 23 | В ролята на речници са били използвани съществуващите *keyword list*-ове, които са просто списъци от наредени двойки във вида `{атом, стойност}`. 24 | Друго нещо, което е било ползвано (и се ползва и до днес) е *ETS*, *key-value* база данни в паметта, която е доста оптимизирана за конкурентен достъп и промени. 25 | За речници с определена структора са били използвани *record* структурите, които са подобни на `struct`-овете в *C*. Всъщност тези *record* структури са просто именувани кортежи. 26 | Можете да научите повече за тях [тук](http://erlang.org/doc/reference_manual/records.html) и [в документацията](https://hexdocs.pm/elixir/Record.html) на *Elixir*-ския им *wrapper* модул. 27 | 28 | Във версия **17** се появяват речниците. 29 | Те са имплементирани като два масива - единият пази ключовете, които са сортирани, а другият - стойностите. 30 | При такава имплементация, речници с повече елементи стават бавни за писане и инициират множество копирания. 31 | Поради тази причина *Elixir* се сдобива със собствена имплементация на речник, `HashDict` 32 | и единен модул за работа с речници - `Dict`. Този речник е по-използваем 33 | и много по бърз от вградения в *Erlang* когато съдържа множество елементи. 34 | 35 | От версия **18** на *Erlang/OTP*, *Erlang* има нова имплементация за *Map*-ове с много елементи. 36 | Тази имплементация е подобна на имплементациите ползвани в *Clojure* и *Scala*. 37 | Използва се бърза за достъп и добра при засичане на колизии структура - *Hash Array Mapped Trie*. 38 | Малко повече можете да прочетете за тази структура [тук](https://idea.popcount.org/2012-07-25-introduction-to-hamt). 39 | 40 | Тази нова имплементация на *Map* идваща от *Erlang* е доста [по-бърза](https://gist.github.com/BinaryMuse/bb9f2cbf692e6cfa4841) 41 | от `HashDict`, затова `HashDict` и `Dict` в момента са _deprecated_. 42 | 43 | Ние ще си говорим за *Map* типа, който се създава с `%{}`. 44 | Не използвайте `HashDict` и `Dict`. 45 | Избягвайте да използвате *keyword lists* там, където е по-правилно да се използва *Map*. 46 | 47 | 48 | ## Създаване и достъп до Map 49 | 50 | Създаваме речник така: 51 | 52 | ```elixir 53 | %{} # Празен 54 | #=> %{} 55 | 56 | %{name: "Пешо"} # С ключ и стойност 57 | #=> %{name: "Пешо"} 58 | ``` 59 | 60 | Ключове могат да бъдат всякакви типове, дори други *Map*-ове. 61 | Най-често се използват атоми и низове. 62 | Когато можем да използваме атоми е най-добре да използваме само атоми, защото те са оптимизиране за *pattern matching*-ване (все едно съпоставяме числа) и ако вече даден атом е дефиниран, той се пре-използва. 63 | Все пак е лошо да конвертираме произволни низове към атоми, защото атомите никога не се зачистват от паметта. 64 | 65 | Пример за *Map* с ключове атоми: 66 | 67 | ```elixir 68 | pesho = %{ 69 | name: "Пешо", 70 | age: 35, 71 | hobbies: {:drink, :smoke, :eurofootball}, 72 | job: "шлосер" 73 | } 74 | 75 | # Четене на стойности: 76 | 77 | pesho[:name] 78 | #=> "Пешо" 79 | 80 | pesho.name 81 | #=> "Пешо" 82 | 83 | Map.fetch(pesho, :name) 84 | #=> {:ok, "Пешо"} 85 | 86 | Map.fetch!(pesho, :name) 87 | #=> "Пешо" 88 | 89 | Map.get(pesho, :name) 90 | #=> "Пешо" 91 | ``` 92 | 93 | Разликата между всички тези начини на достъп е в поведението им, когато ключът не съществува: 94 | 95 | ```elixir 96 | pesho[:full_name] # В този случай връща nil ако го няма ключа 97 | #=> nil 98 | 99 | pesho.full_name # KeyError 100 | #=> ** (KeyError) key :full_name not found 101 | 102 | Map.fetch(pesho, :full_name) # :error ако го няма ключа 103 | #=> :error 104 | 105 | Map.fetch!(pesho, :full_name) # Подобно на pesho.full_name 106 | #=> ** (KeyError) key :full_name not found 107 | 108 | Map.get(pesho, :full_name) # Map.get работи с default стойност, която е nil, ако не е зададена 109 | #=> nil 110 | Map.get(pesho, :full_name, "Петър Петров") 111 | #=> "Петър Петров" 112 | 113 | Map.get_lazy(pesho, :full_name, fn -> "Петър Петров" end) 114 | #=> "Петър Петров" 115 | 116 | # Горната функция се ползва, ако стойността по подразбиране е скъпа за пресмятане. 117 | ``` 118 | 119 | * Използваме `Map.get*` ако искаме да имаме стойност по подразбиране. 120 | * Използваме `Map.fetch!/2` или `.<атом>`, ако искаме да получим грешка при четене на несъществуващ ключ. 121 | * Ако искаме да имаме проверка от типа `{:ok, }` при успех или `:error` при липсващ ключ, ползваме `Map.fetch/2`. 122 | 123 | Защо имаме `Map.get_lazy/3`? Защо `Map.get/3` не е дефиниран с гардове за проверка на стойността по подразбиране дали не е функция. 124 | Отговорът е следният: ако имаме *Map* със стойности функции и искаме да имаме стойност по подразбиране някаква функция, не искаме тя да се изпълни. 125 | Тя трябва да бъде върната като стойност по подразбиране. Именно поради случай като този имаме друго име за 'lazy' поведението. 126 | 127 | Ако ключовете не са атоми, синтаксисът е малко по-различен: 128 | 129 | ```elixir 130 | pesho = %{ 131 | "name" => "Пешо", 132 | "age" => 35, 133 | "hobbies" => {:drink, :smoke, :eurofootball}, 134 | "job" => "шлосер" 135 | } 136 | ``` 137 | 138 | Достъпът `map.` също не работи в този случай: 139 | 140 | ```elixir 141 | pesho["age"] 142 | #=> 35 143 | 144 | pesho.age 145 | #=> ** (KeyError) key :age not found 146 | pesho."age" 147 | #=> ** (KeyError) key :age not found 148 | ``` 149 | 150 | ## "Промяна" на Map 151 | 152 | Премахване на ключове: 153 | 154 | ```elixir 155 | Map.pop(pesho, :name) 156 | #=> {"Пешо", %{age: 35, hobbies: {:drink, :smoke, :eurofootball}, job: "шлосер"}} 157 | 158 | Map.pop(pesho, :full_name, "Петър Панов") 159 | #=> {"Петър Панов", %{age: 35, hobbies: {:drink, :smoke, :eurofootball}, job: "шлосер", name: "Пешо"}} 160 | 161 | Map.pop_lazy(pesho, :nick, fn -> "pe60" end) 162 | #=> {"pe60", %{age: 35, hobbies: {:drink, :smoke, :eurofootball}, job: "шлосер", name: "Пешо"}} 163 | ``` 164 | 165 | С други думи `pop` функциите са много подобни на `get` функциите, но за разлика от тях връщат наредена 2-ка. 166 | Първият елемент е този който е търсен или стойност по подразбиране, а вторият e нов *Map* без дадените ключ и стойност. 167 | 168 | ```elixir 169 | Map.delete(pesho, :name) 170 | #=> %{age: 35, hobbies: {:drink, :smoke, :eurofootball}, job: "шлосер"} 171 | 172 | Map.delete(pesho, :full_name) 173 | #=> %{age: 35, hobbies: {:drink, :smoke, :eurofootball}, job: "шлосер", name: "Пешо"} 174 | ``` 175 | 176 | Функцията `Map.delete/2` просто връща *Map* без указания ключ или оригиналния речник, ако ключът не съществува. 177 | Функцията `Map.drop/2` приема списък от ключове, които да се "премахнат": 178 | 179 | ```elixir 180 | Map.drop(pesho, [:hobbies, :job]) 181 | #=> %{age: 35, name: "Пешо"} 182 | ``` 183 | 184 | ## "Промяна" и "добавяне" на стойности 185 | 186 | Има много начини за "промяна" на речник (тоест да се генерира нов, с някаква разлика спрямо оригинала). 187 | Няколко от тях: 188 | 189 | ```elixir 190 | pesho = %{ name: "Пешо", age: 35 } 191 | #=> %{age: 35, name: "Пешо"} 192 | 193 | # Map.put/3 добавя дадена стойност за даден ключ, ако ключът съществува ресултатът е с променена стойност. 194 | Map.put(pesho, :full_name, "Петрун Петрунов") 195 | #=> %{age: 35, full_name: "Петрун Петрунов", name: "Пешо"} 196 | ``` 197 | 198 | Функциите `Map.put_new/3` и `Map.put_new_lazy/3` правят същото като `Map.put/3`, 199 | с тази разлика, че ако указаният ключ съществува, речникът който бива върнат е същият, без променена стойност. 200 | 201 | Интересен начин за промяна, но само на вече съществуващи в речника ключове е следният: 202 | 203 | ```elixir 204 | pesho = %{ 205 | name: "Пешо", 206 | age: 35, 207 | hobbies: {:drink, :smoke, :eurofootball}, 208 | job: "шлосер" 209 | } 210 | 211 | %{pesho | hobbies: :none} 212 | #=> %{age: 35, hobbies: :none, job: "шлосер", name: "Пешо"} 213 | 214 | %{pesho | drink: :rakia} # Ако не съществува - грешка. 215 | #=> ** (KeyError) key :drink not found in: 216 | ``` 217 | 218 | По този начин могат да се променят множество ключове, 219 | а ако искаме да променим и едновременно добавим нови ключове, ползваме `Map.merge/2`: 220 | 221 | ```elixir 222 | Map.merge(pesho, %{hobbies: :just_drinking, drink: :rakia}) 223 | #=> %{age: 35, drink: :rakia, hobbies: :just_drinking, job: "шлосер", name: "Пешо"} 224 | ``` 225 | 226 | В модула `Map` има още няколко функции за "промяна", които няма да разгледаме тук, 227 | но са налични в [документацията](https://hexdocs.pm/elixir/Map.html). 228 | 229 | ## Речници и съпоставяне на образци 230 | 231 | Речниците имат интересно поведение при съпоставяне. 232 | При съпоставяне не е нужно лявата и дясната страна да съвпадат изцяло. 233 | Важното е ключовете и стойностите от ляво да съвпадат с **под-множество** на тези от дясно. 234 | 235 | ```elixir 236 | pesho = %{age: 35, drink: :rakia, hobbies: :just_drinking, name: "Пешо"} 237 | #=> %{age: 35, drink: :rakia, hobbies: :just_drinking, name: "Пешо"} 238 | 239 | %{name: x} = pesho 240 | #=> %{age: 35, drink: :rakia, hobbies: :just_drinking, name: "Пешо"} 241 | 242 | x 243 | #=> "Пешо" 244 | ``` 245 | 246 | Можем да направим проверка, че дадени ключове съществуват по следния начин: 247 | 248 | ```elixir 249 | %{name: _, age: _} = pesho 250 | #=> %{age: 35, drink: :rakia, hobbies: :just_drinking, name: "Пешо"} 251 | 252 | %{name: _, age: _, location: _} = pesho 253 | #=> ** (MatchError) no match of right hand side value: %{age: 35, drink: :rakia, hobbies: :just_drinking, name: "Пешо"} 254 | ``` 255 | 256 | *Pattern matching*-ът не може да се приложи на ключове: 257 | 258 | ```elixir 259 | %{x => "Пешо"} = %{"name" => "Пешо"} 260 | #=> ** (CompileError) illegal use of variable x inside map key match, maps can only match on existing variable by using ^x 261 | ``` 262 | 263 | Имаме същото поведение, когато речник е подаден като аргумент при извикването на функция: 264 | 265 | ```elixir 266 | defmodule A do 267 | def f(%{name: name} = person) do 268 | IO.puts(name) 269 | 270 | person 271 | end 272 | end 273 | 274 | A.f(pesho) 275 | #output: Пешо 276 | #=> %{age: 35, drink: :rakia, hobbies: :just_drinking, name: "Пешо"} 277 | ``` 278 | 279 | В този пример, `name` стойността на подадения речник беше *match*-ната и използвана в тялото на функцията. 280 | 281 | ## Операции върху вложени речници 282 | 283 | Нека имаме следния речник: 284 | 285 | ```elixir 286 | data = %{ 287 | proboscidea: %{ 288 | elephantidae: %{ 289 | elephas: ["Asian Elephant", "Indian Elephant", "Sri Lankan Elephant"], 290 | loxodonta: ["African bush elephant", "African forest elephant"] 291 | }, 292 | mammutidae: %{ 293 | mammut: ["Mastodon"] 294 | } 295 | } 296 | } 297 | ``` 298 | 299 | Най-лесният начин да добавим нещо на по-дълбоко ниво е `Kernel.put_in/3`: 300 | 301 | ```elixir 302 | put_in(data, [:proboscidea, :elephantidae, :fictional], ["Jumbo"]) 303 | #=> %{ 304 | #=> proboscidea: %{ 305 | #=> elephantidae: %{ 306 | #=> elephas: ["Asian Elephant", "Indian Elephant", "Sri Lankan Elephant"], 307 | #=> fictional: ["Jumbo"], 308 | #=> loxodonta: ["African bush elephant", "African forest elephant"] 309 | #=> }, 310 | #=> mammutidae: %{ mammut: ["Mastodon"] } 311 | #=> } 312 | #=> } 313 | 314 | # Имаме същия резултат, ако ключът 'fictional' съществуваше: 315 | put_in(data.proboscidea.elephantidae.fictional, ["Jumbo"]) 316 | ``` 317 | 318 | По подобен начин можем да прочетем дълбоко вложена стойност: 319 | 320 | ```elixir 321 | get_in(data, [:proboscidea, :elephantidae, :loxodonta]) 322 | #=> ["African bush elephant", "African forest elephant"] 323 | ``` 324 | -------------------------------------------------------------------------------- /posts/materials/metaprogramming_part1.md: -------------------------------------------------------------------------------- 1 | --- 2 | title_image_path: metaprogramming.jpg 3 | category: Програма 4 | author: valo 5 | tags: 6 | - elixir 7 | - metaprogramming 8 | --- 9 | 10 | # Meta-програмиране в Elixir част 1 11 | 12 | Какво всъщност е мета програмиране? 13 | Накратко, това е възможноста да пишем код, който пише код. 14 | Тъй като това е една от онези дефиниции, които не успяват много успешно да обяснят за какво точно става въпрос, нека да видим няколко примера за приложения на мета програмирането. 15 | 16 | ## Автоматизирано създаване на функции 17 | 18 | Мета програмирането позволява да се автоматизира генерирането на множество функции с подобна или една и съща имплементация. Нека например си представим как би изглеждало генерирането на HTML като код на Elixir: 19 | 20 | ```elixir 21 | html do 22 | head do 23 | title do 24 | text "Hello To Our HTML DSL" 25 | end 26 | end 27 | body do 28 | h1 class: "title" do 29 | text "Introduction to metaprogramming" 30 | end 31 | p do 32 | text "Metaprogramming with Elixir is really awesome!" 33 | end 34 | end 35 | end 36 | ``` 37 | 38 | Принципно нищо не ни пречи да имплементираме горните функции без мета програмиране, но ще трябва за всеки HTML таг да напишем функция, която да прави едно и също. Много по-лесно би било да имаме списък от всички тагове в един файл и от него да генерираме всички нужни функции. Това може да стане лесно с мета програмиране и няма да ни е нужен никакъв допълнителен инструмент, освен Elixir. В други езици, подобен проблем обикновенно се решава чрез инструменти като Makefiles и допълнителни скриптове. 39 | 40 | ## Дефиниране на DSL-и 41 | 42 | Мета програмирането позволява да дефинираме нови "езици", които решават някакъв специфичен проблем. Нека например видим библиотеката Ecto, която позволява да се пишат SQL заявки като код на Elixir: 43 | 44 | ```elixir 45 | from o in Order, 46 | where: o.created_at > ^Timex.shift(DateTime.utc_now(), days: -2) 47 | join: i in OrderItems, on: i.order_id == o.id 48 | ``` 49 | 50 | Горният код ще генерира SQL от следният вид: 51 | 52 | ```SQL 53 | SELECT o.* 54 | FROM orders o 55 | JOIN order_items i ON i.order_id = o.id 56 | WHERE o.created_at > '2017-04-28 14:15:34' 57 | ``` 58 | 59 | Както виждате можем да пишем SQL код директно в Elixir! Забележете, че това е синтактично валиден код, но без да се пренапише няма да може да се компилира. Тук дори и да искаме няма да може да се разминем от мета програмирането за да постигнем горният синтаксис. Това което макроса `from` прави е да пренапише аргументите, които му се подават към специална структура, която после се използва за да се конструира финалният SQL. Преимуществата на горния подход, са че заявките могат много лесно да се разделят на малки, части които да се комбинират: 60 | 61 | ```elixir 62 | def orders(from_date) do 63 | from o in Order, 64 | where: o.created_at > ^from_date 65 | join: i in OrderItems, on: i.order_id == o.id 66 | end 67 | 68 | def user_orders(from_date, user_id) do 69 | from o in orders(from_date), 70 | where: o.user_id == ^user_id 71 | end 72 | ``` 73 | 74 | Както виждате във функцията `user_orders`, където искаме да имаме допълнително филтриране по потребителя направил заявката, можем да преизползваме функцията `orders` и единствено да добавим филтрирането по потребител. 75 | 76 | Използването на DSL език за заявки към базата данни има и други преимущества: 77 | 78 | * Автоматично санитизиране на данните и защита от SQL injections 79 | * Валидиране на заявките по време на компилация 80 | * По-лесна поддръжка на различни бази данни 81 | 82 | ## Въведение в Abstract Syntax Tree 83 | 84 | Мета програмирането в Elixir става чрез дефинирането на макроси. Това са функции, които приемат като аргументи код и връщат код като резултат, като кода е във формата на Abstract Syntax Tree или AST. 85 | 86 | Почти всеки един език за програмиране по един или по друг начин използва AST, но в много случаи потребителите на езика нямат достъп до него. Обикновенно AST се използва като междинно представяне преди кода да бъде компилиран до машинни инструкции. В Elixir обаче, не само че имаме достъп до AST, но и това AST е представено чрез стандартните структури от данни, с които вече се запознахме. 87 | 88 | Реално мета програмирането се извършва чрез манипулация на AST-то генерирано по време на компилация и затова е и много важно да запомним, че мета програмирането се извършва **само по време на компилация**. Това значи, че веднъж компилиран, кода не може да бъде променян, за разлика от Ruby например. Това може да звучи, като сериозно ограничение, но реално се оказва, че е напълно достатъчно в повечето случаи и при всички положения подобрява бързината на езика многократно. Все още не съм установил ситуация, където да ми е трябвала манипулация на кода по време на изпълнение и мисля, че авторите на езика са направили много добър trade-off. 89 | 90 | Нека да разгледаме малко примери за това как изглежда AST. За да получим AST на някакъв Elixir код, можем да използваме макроса `quote`. Например: 91 | 92 | ```elixir 93 | iex> quote do: 1 + 2 94 | {:+, [context: Elixir, import: Kernel], [1, 2]} 95 | iex> quote do: div(10, 2) 96 | {:div, [context: Elixir, import: Kernel], [10, 2]} 97 | ``` 98 | 99 | Както виждате, AST представлява кортежи от тройки, които имат следният формат: 100 | 101 | ``` 102 | {<име на функция>, <контекст>, <списък от аргументи>} 103 | ``` 104 | 105 | Този формат може много да ви напомни на Lisp и ще сте напълно прави. Реално в Lisp, кода се представя по много подобен начин и от там идва и идеята, че в Lisp кода всъщност са просто данни. За разлика от Lisp обаче, в Elixir имаме приятен синтаксис, който се преобразува в AST от компилатора и само ако искаме да правим мета програмиране се налага да работим със странния "префиксен" начин да представяне на код. 106 | 107 | Да видим някои по-сложни примери: 108 | 109 | ```elixir 110 | iex> quote do: 1 + 2 * 3 111 | {:+, [context: Elixir, import: Kernel], 112 | [1, {:*, [context: Elixir, import: Kernel], [2, 3]}]} 113 | ``` 114 | 115 | Ако разпишем горното дърво ще видим, че приоритета на операциите е правилен: 116 | 117 | ```elixir 118 | {:+, _, [ 119 | 1, 120 | {:*, _, [2, 3]} 121 | ]} 122 | ``` 123 | 124 | Както виждате умножението е в отделно под-дърво от събирането. Да видим как би изглеждало AST-то на HTML езика, който разгледахме по-рано: 125 | 126 | ```elixir 127 | iex> quote do 128 | ...> html do 129 | ...> head do 130 | ...> title do 131 | ...> text "Hello To Our HTML DSL" 132 | ...> end 133 | ...> end 134 | ...> end 135 | ...> end 136 | {:html, [], 137 | [[do: {:head, [], 138 | [[do: {:title, [], [[do: {:text, [], ["Hello To Our HTML DSL"]}]]}]]}]]} 139 | ``` 140 | 141 | Тъй като нашият DSL е валиден Elixir код, то той може да бъде компилиран до AST. Респективно ние можем да модифицираме това AST към друго валидно AST, което вече компилатора ще преобразува във BEAM byte code. Нека да видим как става това модифициране на AST-то. 142 | 143 | ## Въведение в макросите 144 | 145 | Макросите са функции, които приемат като аргумент AST и връщат AST. Нека да разгледаме един най-прост пример: да дефинираме макрос, който обръща плюс с минус и умножение с деление. 146 | 147 | ```elixir 148 | defmodule MathChaosMonkey do 149 | defmacro swap_ops(do: {:+, context, arguments}) do 150 | {:-, context, arguments} 151 | end 152 | 153 | defmacro swap_ops(do: {:-, context, arguments}) do 154 | {:+, context, arguments} 155 | end 156 | 157 | defmacro swap_ops(do: {:/, context, arguments}) do 158 | {:*, context, arguments} 159 | end 160 | 161 | defmacro swap_ops(do: {:*, context, arguments}) do 162 | {:/, context, arguments} 163 | end 164 | end 165 | ``` 166 | 167 | Нека сега да тестваме нашия макрос: 168 | 169 | ```elixir 170 | iex> MathChaosMonkey.swap_ops do 171 | ...> 1 + 2 172 | ...> end 173 | -1 174 | iex> MathChaosMonkey.swap_ops do 175 | ...> 10 * 2 + 1 176 | ...> end 177 | 19 178 | iex> MathChaosMonkey.swap_ops do 179 | ...> 10 * 2 180 | ...> end 181 | 5.0 182 | ``` 183 | 184 | Както виждаме успяхме да сътворим пълна бъркотия в аритметичните операции. Нещо, което е важно да се отбележи е че за разлика от езици като Ruby, макросите имат много ясно поле на действие, т.е. няма как да направим глобална промяна във runtime-а. Всички макроси имат ефект единствено в модулите, които са require-нати и използвани. 185 | 186 | Примерът по-горе е доста опростен и напълно безполезен в реални условия. За да можем да дефинираме използваеми макроси по лесен начин, има помощни функции, които ни позволяват да работим без да слизаме на ниво AST структурата. Това е функцията `unquote`, която взема AST структура и я интерпретира в текущия контекст. Нека да разгледаме един пример: 187 | 188 | ```elixir 189 | iex> value = 12 190 | 12 191 | iex> quote do 192 | ...> 1 + 2 * value 193 | ...> end 194 | {:+, [context: Elixir, import: Kernel], 195 | [1, {:*, [context: Elixir, import: Kernel], [2, {:value, [], Elixir}]}]} 196 | iex> quote do 197 | ...> 1 + 2 * unquote(value) 198 | ...> end 199 | {:+, [context: Elixir, import: Kernel], 200 | [1, {:*, [context: Elixir, import: Kernel], [2, 12]}]} 201 | ``` 202 | 203 | Нека да разгледаме друг пример, които ще дефинира функции за умножение на числа по някаква стойност: 204 | 205 | ```elixir 206 | defmodule Multiplier do 207 | defmacro of(value) do 208 | quote do 209 | def unquote(:"multiplier_#{value}")(expr) do 210 | expr * unquote(value) 211 | end 212 | end 213 | end 214 | end 215 | 216 | defmodule Math do 217 | require Multiplier 218 | 219 | Multiplier.of(5) 220 | end 221 | 222 | Math.multiplier_5(2) # => 10 223 | ``` 224 | 225 | Както виждате успяхе да напишем макрос, който да генерира функция, която има поведение зависещо от аргументите, които подадохме на макроса. 226 | 227 | ## Дефиниция на while 228 | 229 | Нека да разгледаме един по-интересен пример: да дефинираме `while` цикъл. Това ще рече, да подкараме следният код в Elixir: 230 | 231 | ```elixir 232 | defmodule Fib do 233 | def async_fib(n) do 234 | spawn(fn -> fib(n) end) 235 | end 236 | 237 | def sync_fib(n) do 238 | pid = async_fib(n) 239 | while(Process.alive?(pid)) do 240 | IO.puts "Waiting..." 241 | sleep(1) 242 | end 243 | IO.puts "Done!" 244 | end 245 | 246 | defp fib(0), do: 0 247 | defp fib(1), do: 1 248 | defp fib(n), do: fib(n-1) + fib(n-2) 249 | end 250 | ``` 251 | 252 | ```elixir 253 | defmodule Loops do 254 | defmacro while(expression, do: block) do 255 | quote do 256 | try do 257 | for _ <- Stream.cycle([:ok]) do 258 | if unquote(expression) do 259 | unquote(block) 260 | else 261 | throw :break 262 | end 263 | end 264 | catch 265 | :break -> :ok 266 | end 267 | end 268 | end 269 | end 270 | 271 | defmodule Fib do 272 | require Loops 273 | 274 | def async_fib(n) do 275 | spawn(fn -> fib(n) end) 276 | end 277 | 278 | def sync_fib(n) do 279 | pid = async_fib(n) 280 | Loops.while(Process.alive?(pid)) do 281 | IO.puts "Waiting..." 282 | Process.sleep(1000) 283 | end 284 | IO.puts "Done!" 285 | end 286 | 287 | defp fib(0), do: 0 288 | defp fib(1), do: 1 289 | defp fib(n), do: fib(n-1) + fib(n-2) 290 | end 291 | 292 | iex> Fib.sync_fib(40) 293 | Waiting... 294 | Waiting... 295 | Waiting... 296 | Waiting... 297 | Waiting... 298 | Waiting... 299 | Waiting... 300 | Done! 301 | :ok 302 | ``` 303 | 304 | Както виждате успяхме да създадем `while` конструкция, която е подобна на циклите в другите езици. Добре е да се отбележи, че следният код няма да работи: 305 | 306 | ```elixir 307 | defmodule Bottles do 308 | def sing(n) do 309 | i = 0 310 | while(i < n) do 311 | IO.puts "#{n} bottles hanging on the wall" 312 | IO.puts "If one bottle crashes on the floor, there will be..." 313 | i = i - 1 # Won't change the binding in the condition of the loop 314 | end 315 | IO.puts "No bottles hanging on the wall" 316 | end 317 | end 318 | ``` 319 | 320 | Тъй като променливите има строго дефиниран scope и данните са неизменими, в горния пример `i = i - 1` няма да промени условието `i < 10`, тъй като `unquote` ще вземе стойността на `i` преди `while` макроса и ако променим тази стойност вътре в болка, то тя няма да се отрази при втория цикъл. За да илюстрираме по-ясно това нека да пренапишем горния макрос с рекурсия: 321 | 322 | ```elixir 323 | defmodule Loops do 324 | defmacro while(expr, do: block) do 325 | quote do 326 | Loops.run_loop(fn -> unquote(expr) end, fn -> unquote(block) end) 327 | end 328 | end 329 | 330 | def run_loop(expr_body, loop_body) do 331 | case expr_body.() do 332 | true -> 333 | loop_body.() 334 | run_loop(expr_body, loop_body) 335 | _ -> 336 | :ok 337 | end 338 | end 339 | end 340 | ``` 341 | 342 | В горната имплементация си разделяме цикъла на условие и на тяло и ги "обвиваме" във функции, за да можем да ги изпълняваме когато искаме. Всяка от тази функции си има локален binding на променливите с нея и този binding не може да бъде променян от външните функции. 343 | -------------------------------------------------------------------------------- /posts/materials/structs.md: -------------------------------------------------------------------------------- 1 | --- 2 | title_image_path: structs.jpg 3 | category: Програма 4 | created_at: 2019-03-07T17:23:12 5 | tags: 6 | - elixir 7 | - map 8 | - pattern matching 9 | - dict 10 | - struct 11 | - module 12 | --- 13 | 14 | # Структури 15 | 16 | С помощта на речниците в _Elixir_ можем да създаваме нещо като свои собствени типове. 17 | Това са _Map_-ове с име и точно определени ключове, които са обвързани с модул, чиито функции обикновено боравят с тях. 18 | 19 | Почти всички функции за работа с речници могат да работят със структури, затова е добре да сте запознати с публикацията на тема [речници](https://elixir-lang.bg/materials/posts/maps), преди да прочетете тази. 20 | 21 | ## Дефиниция 22 | 23 | Структурите, както казахме, представляват ограничени речници. 24 | Те са ограничени по няколко признака: 25 | 26 | 1. Ключовете им задължително са атоми. 27 | 2. Ключовете им са предефинирани. 28 | 3. Много свойства на речниците не са валидни за структури. Да речем, достъп от сорта на `some_struct[:some_atom]` е невъзможен. 29 | 30 | Структурите се дефинират в модул. 31 | Името на модула е името на структурата: 32 | 33 | ```elixir 34 | defmodule Person do 35 | defstruct [:name, :age, location: "Far away", children: []] 36 | end 37 | ``` 38 | 39 | Създаваме структурата `Person` с четири полета. 40 | Полетата `name` и `age` нямат зададена определена _default_-на стойност, затова са `nil` по подразбиране. 41 | Създаваме инстанция на структурата по начин, подобен на този, с който създаваме речници: 42 | 43 | ```elixir 44 | pesho = %Person{name: "Пешо", age: 35, location: "Горен Чвор"} 45 | #=> %Person{age: 35, children: [], location: "Горен Чвор", name: "Пешо"} 46 | ``` 47 | 48 | Лесно можем да видим какво представлява `pesho`: 49 | 50 | ```elixir 51 | inspect(pesho, structs: false) 52 | #=> "%{__struct__: Person, age: 35, children: [], location: \"Горен Чвор\", name: \"Пешо\"}" 53 | ``` 54 | 55 | Както виждаме, има скрит ключ `__struct__` със стойност името на структурата. 56 | Другата разлика с динамичните речници е, че структурите не имплементират някои протоколи, които ще видим в следващата статия. 57 | Хубава новина е, че `Map` модулът работи със структури: 58 | 59 | ```elixir 60 | Map.put(pesho, :name, "Стойчо") 61 | #=> %Person{age: 35, children: [], location: "Горен Чвор", name: "Стойчо"} 62 | ``` 63 | 64 | Операторът за обновяване също работи: 65 | 66 | ```elixir 67 | %{pesho | name: "Стойчо"} 68 | #=> %Person{age: 35, children: [], location: "Горен Чвор", name: "Стойчо"} 69 | 70 | ``` 71 | 72 | Също така: 73 | 74 | ```elixir 75 | is_map(pesho) 76 | #=> true 77 | is_map(%{"key" => "value"}) 78 | #=> true 79 | 80 | map_size(pesho) 81 | #=> 5 82 | map_size(%{"ключ" => "стойност"}) 83 | #=> 1 84 | ``` 85 | 86 | Тези две `Kernel` функции проверяват съответно дали стойност е речник и колко ключа има даден речник. 87 | Функцията `Kernel.is_map/1` връща `true` и за структури. 88 | А функцията `Kernel.map_size/1` връща **5**, макар `pesho` да има само **4** дефинирани ключа. 89 | Това е така, защото тя брои и скрития `__struct__` ключ. 90 | 91 | Можем да съпоставяме структури с речници и други структури от същия тип: 92 | 93 | ```elixir 94 | %{name: x} = pesho 95 | #=> %Person{age: 35, children: [], location: "Горен Чвор", name: "Пешо"} 96 | x 97 | #=> "Пешо" 98 | 99 | # Същото поведение: 100 | %Person{name: x} = pesho 101 | #=> %Person{age: 35, children: [], location: "Горен Чвор", name: "Пешо"} 102 | x 103 | #=> "Пешо" 104 | 105 | #Но това не е валидно: 106 | %Person{} = %{} 107 | #=> ** (MatchError) no match of right hand side value: %{} 108 | ``` 109 | 110 | Последното поведение е нормално, защото `Person` има `__struct__` ключ, както и стойности по подразбиране. 111 | Поради това никоя структура отляво не може да се съпостави с речник отдясно. 112 | Обратното е възможно. 113 | 114 | Понякога искаме при създаването на дадена структура задължително да се задават стойности за даден ключ, или с други думи, да имаме задължителни полета. 115 | Това можем да постигнем чрез модулния атрибут `@enforce_keys`: 116 | 117 | ```elixir 118 | defmodule Person do 119 | @enforce_keys [:age, :name] 120 | defstruct [:name, :age, location: "Far away", children: []] 121 | end 122 | 123 | %Person{name: "Гошо"} 124 | #=> ** (ArgumentError) the following keys must also be given when building struct Person: [:age] 125 | #=> expanding struct: Person.__struct__/1 126 | 127 | %Person{name: "Гошо", age: 58, children: [%Person{name: "Пешо", age: 34}]} 128 | #=> %Person{ 129 | #=> age: 58, 130 | #=> children: [ 131 | #=> %Person{age: 34, children: [], location: "Far away", name: "Пешо"} 132 | #=> ], 133 | #=> location: "Far away", 134 | #=> name: "Гошо" 135 | #=> } 136 | ``` 137 | 138 | ## За какво и как да ползваме структури 139 | 140 | Структурите се дефинират в модул – с идеята, че са нещо като тип, дефиниран от нас. 141 | В модула би трябвало да напишем специални функции, които да работят с този тип. Така имаме на едно място дефиницията на типа и функциите за работа с него. 142 | 143 | Пример е `MapSet`: 144 | 145 | ```elixir 146 | inspect(MapSet.new([2, 2, 3, 4]), structs: false) 147 | #=> "%{__struct__: MapSet, map: %{2 => true, 3 => true, 4 => true}}" 148 | ``` 149 | 150 | Това е структура. Вътрешно е представена с речник под ключа `map`. 151 | За да има уникални стойности, тоест да е множество, използва ключовете на този речник като свои стойности. 152 | При ключовете на речник, както знаем, няма повторения, затова той работи и стойностите са просто `true`. 153 | 154 | ```elixir 155 | MapSet.union(MapSet.new([1, 2, 3]), MapSet.new([2, 3, 4])) 156 | #=> #MapSet<[1, 2, 3, 4]> 157 | ``` 158 | 159 | Както споменахме, самият модул `MapSet` дефинира набор от функции за работа със структурата `MapSet`. 160 | 161 | Това не означава, че можем да гледаме на структурите като на класове и на функциите в модулите им като на техни методи. 162 | Структурите не са класове. 163 | _Elixir_ не е обектно-ориентиран език. 164 | Структурите са речници, които имат предварително дефинирана структура. 165 | Те са _immutable_ и няма как тяхното състояние да се промени. 166 | 167 | Ако създадем структура `Post`, която представлява блог пост, ние просто създаваме речник с предварително дефинирани полета, представляващ публикация. 168 | В модула му ще има функции за създаване на `Post` от файл или от друг източник, като и функции за някаква валидация. 169 | Няма нужда от сложни взаимодействия между структури, защото всичко e _immutable_. 170 | Действията, с които разполагаме, са прости, малки функции, които композираме. 171 | 172 | Структурите задават определена форма на данни, чиято конструкция познаваме. 173 | Ако ги комбинираме с тип-спецификациите, за които ще говорим в отделна публикация, получаваме добра документация на данните, с които работим. 174 | Именно поради това можем да приемаме структурите като дефинирани от нас типове. 175 | 176 | ## Някои структури, идващи с Elixir 177 | 178 | В предишната секция ви показахме `MapSet`, която представлява множество. 179 | [Модулът на тази структура](https://hexdocs.pm/elixir/MapSet.html#content) съдържа функции за работа с множества като сечения, разлики, сравнения. 180 | 181 | Друга такава структура е поредицата (`Range`), която вече сме показвали. 182 | Всъщност, когато говорихме за [енумерации](https://elixir-lang.bg/materials/posts/enum_and_stream), споменахме както `Range`, така и `MapSet`. 183 | Как можем да направим наша структура енумерация, ще видим в следващата публикация. 184 | 185 | `Range` е поредица с начало и край: 186 | 187 | ```elixir 188 | range = 1..10 189 | #=> 1..10 190 | 191 | inspect(range, structs: false) 192 | #=> "%{__struct__: Range, first: 1, last: 10}" 193 | 194 | first..last = range 195 | #=> 1..10 196 | first 197 | #=> 1 198 | last 199 | #=> 10 200 | ``` 201 | 202 | Поредицата е от цели числа, като ако я обходим, ще минем през всички цели числа от началото до края ѝ: 203 | 204 | ```elixir 205 | range = 1..100 206 | #=> 1..100 207 | 208 | # Сума от 1 до 100: 209 | Enum.reduce(range, 0, fn n, acc -> n + acc end) 210 | #=> 5050 211 | ``` 212 | 213 | Както знаем, _unicode codepoint_-ите са цели числа, така че: 214 | 215 | ```elixir 216 | ?a..?z 217 | #=> 97..122 218 | 219 | ?а..?я 220 | #=> 1072..1103 221 | 222 | Enum.map(?a..?z, &(&1 - 32)) |> Enum.map(&<< &1::utf8 >>) 223 | #=> ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", 224 | #=> "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"] 225 | ``` 226 | 227 | Можем и да проверим дали дадено число се съдържа в дадена поредица: 228 | 229 | ```elixir 230 | 5 in 1..10 231 | #=> true 232 | ``` 233 | 234 | В _Elixir_ регулярните изрази са имплементирани със структура и функциите за работа с тях са в модула [`Regex`](https://hexdocs.pm/elixir/Regex.html#content). 235 | Има съкратен синтаксис за създаване на регулярни изрази, но може да се използва и модулът: 236 | 237 | ```elixir 238 | phone_number = ~r/^\+?[\d\s]{3,}$/ 239 | #=> ~r/^\+?[\d\s]{3,}$/ 240 | 241 | 242 | {:ok, phone_number} = Regex.compile("^\\+?[\\d\\s]{3,}$") 243 | #=> {:ok, ~r/^\+?[\d\s]{3,}$/} 244 | 245 | inspect(phone_number, structs: false) 246 | #=> %{ 247 | #=> __struct__: Regex, 248 | #=> opts: "", 249 | #=> re_pattern: {:re_pattern, 0, 0, 0, 250 | #=> <<69, 82, 67, 80, 113, 0, 0, 0, 16, 0, 0, 0, 1, 0, 0, 0, 255, 255, 255, 255, 251 | #=> 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 252 | #=> ...>>}, 253 | #=> re_version: "8.41 2017-07-05", 254 | #=> source: "^\\+?[\\d\\s]{3,}$" 255 | #=> } 256 | ``` 257 | 258 | Има и специален оператор за _match_-ване на регулярни изрази, `=~`: 259 | 260 | ```elixir 261 | "+359 887 172 213" =~phone_number 262 | #=> true 263 | 264 | "0887172213" =~ phone_number 265 | #=> true 266 | 267 | "31" =~ phone_number 268 | #=> false 269 | ``` 270 | 271 | Няма да навлизаме в подробности за регулярните изрази. За повече информация погледнете [документацията](https://hexdocs.pm/elixir/Regex.html#content) на модула `Regex`. 272 | Регулярните изрази на _Elixir_ ползват модула на _Erlang_ `:re` и са _perl compatible_. 273 | 274 | Други структури, които ще ползвате често, са [`Date`](https://hexdocs.pm/elixir/Date.html#content), [`Time`](https://hexdocs.pm/elixir/Time.html#content) и [`DateTime`](https://hexdocs.pm/elixir/DateTime.html#content). 275 | Ето пример за `Time`: 276 | 277 | ```elixir 278 | time = ~T[23:00:07.001] 279 | #=> ~T[23:00:07.001] 280 | 281 | inspect(time, structs: false) 282 | #=> %{ 283 | #=> __struct__: Time, 284 | #=> calendar: Calendar.ISO, 285 | #=> hour: 23, 286 | #=> microsecond: {1000, 3}, 287 | #=> minute: 0, 288 | #=> second: 7 289 | #=> } 290 | ``` 291 | 292 | Има и много други структури, идващи с езика, както и такива, които ще срещате и използвате, когато използвате различни библиотеки. 293 | В следващата статия ще разберем как да направим така, че различни структури да бъдат подавани на една и съща функция, а също и – спрямо това коя е структурата – да се изпълнява различна логика – по начин, който е разширим за нови структури. 294 | -------------------------------------------------------------------------------- /posts/materials/why_elixir.md: -------------------------------------------------------------------------------- 1 | --- 2 | title_image_path: why_elixir.jpg 3 | created_at: 2019-02-21T00:09:23 4 | category: Програма 5 | tags: 6 | - elixir 7 | - introduction 8 | - erlang 9 | - beam 10 | - functional programming 11 | --- 12 | 13 | # Защо да учим и използваме Elixir? 14 | 15 | Това е блог пост към първата лекция[1](http://gitpitch.com/ElixirCourse/presentations-2019/?p=welcome) от курса _Функционално програмиране с Elixir_, провеждан във ФМИ през летния семестър на 2018/2019 година. В него ще отговорим на въпроса защо бихте учили и използвали Elixir. Но първо малко предистория. 16 | 17 | ### Erlang 18 | 19 | Erlang е функционален език, разработен в средата на 80-те години от Ериксон, с цел писане на телеком програми. Въпреки че мотивацията за създаването му е водена от спецификата на сферата на телекомуникациите, Erlang не се използва само в тази област. Eдна от по-рядко споменаваните причини[2](https://www.erlang-factory.com/upload/presentations/416/MikeWilliams.pdf) за създаването му е желанието да се заменят използваните дотогава специализиран хардуер и софтуер (включително и операционна система). Erlang няма експлицитна поддръжка за програмиране на телефони или каквото и да е било друго телеком оборудване. Няма и изисквания за специален хардуер и операционна система. Вместо това езикът предлага решения за техническите изисквания и проблеми, срещани при създаването на подобен тип софтуер: 20 | 21 | - конкурентност; 22 | - скалируемост; 23 | - дистрибутираност; 24 | - толерантност към грешки; 25 | - обновления без спиране на програмата; 26 | - бързо отговаряне на всички заявки дори при високо натоварване; 27 | 28 | Всичко това се случва във време, когато интернет не е широко разпространен и повечето програми работят на един компютър и без връзка с интернет. При този вид софтуер няма изисквания за дистрибутираност и хоризонтална скалируемост. По това време персоналните компютри не са разполагали и с 2-, 4- или 16-ядрени процесори - реално изискване софтуерът автоматично да започне да работи по-бързо при добавяне на още процесори/ядра не е съществувало. Директни следствия от това са дизайнът и предназначението на останалите програмни езици, разработени по това време или по-рано, както и езиците, базирани на тях. 29 | 30 | Дори никога да не използвате Erlang, то идеите и подходът[3](https://ferd.ca/the-zen-of-erlang.html) му към решаване на определен тип проблеми са напълно приложими при използването на други езици. 31 | 32 | ### Къде е мястото на Erlang днес? 33 | 34 | Добре, но колко от нас пишат софтуер за телекоми или софтуер с подобни изисквания? Всъщност доста от нас. Изискванията към съвременните уеб приложения се припокриват напълно с изискванията, с които са се съобразили създателите на Erlang. Голямо предимство е да имаш език и платформа, които са изградени на база задоволяване на тези изисквания, без нуждата от външни библиотеки, които да имплементират липсващата функционалност в езика и които заобикалят неговите недостатъци. 35 | 36 | ![Ad-hoc Erlang implementation](https://raw.githubusercontent.com/ElixirCourse/blog/master/assets/erlang_ad_hoc_implement.png) 37 | 38 | Ако искате да разберете повече за гаранциите, които ни дава BEAM[4](http://www.erlang-factory.com/upload/presentations/708/HitchhikersTouroftheBEAM.pdf) - виртуалната машина, върху която се изпълнява Erlang, - то най-бързият начин е да изгледате [това видео](https://www.youtube.com/watch?v=5SbWapbXhKo). 39 | 40 | ### Elixir 41 | 42 | Защо говорим толкова за Erlang в статия, която носи името "Защо да учим и използваме **Elixir?**" 43 | ![Best part of Elixir tweet](https://raw.githubusercontent.com/ElixirCourse/blog/master/assets/elixir_most_important_part.png) 44 | 45 | Elixir е функционален език. Разработката му започва през 2011 г., а версия 1.0 се появява през септември 2014 г. 46 | 47 | Elixir се изпълнява върху BEAM. Той може да използва всичко вече написано, както и всичко, което ще бъде написано на Erlang. Това ни дава възможността да съчетаваме нов и модерен език с библиотеки, издържали теста на времето. Извикването на Erlang код от Elixir не носи допълнително забавяне и нужда от използване на криптичен синтаксис или преобразувания на типове. Нека сравним разликите на извикване на Elixir и Erlang функция от даден модул: 48 | 49 | Elixir: 50 | 51 | ```elixir 52 | DateTime.utc_now() 53 | ``` 54 | 55 | Erlang: 56 | 57 | ```erlang 58 | :crypto.strong_rand_bytes(64) 59 | ``` 60 | 61 | Elixir модулите започват с главна буква, а Erlang модулите започват с `:`и малка буква. 62 | 63 | Нека да разгледаме една малка част от нещата, заради които _ние_ харесваме Elixir/Erlang: 64 | 65 | - Функционален език - непроменими (immutable) и персистентни (persistent) структури от данни, съпоставяне на образци (pattern matching), функции от по-висок ред, композиция на функции. 66 | - Баланс между чисти (pure) и функции със странични ефекти. Тук някои от заклетите фенове на Haskell може да не се съгласят. Но някои неща трябва да бъдат пожертвани на олтара на продуктивността. 67 | - Процеси на ниво език - може би звучи скучно, но това е основата, на която се изгражда толкова известната конкурентност и дистрибутираност на Erlang/Elixir. 68 | - OTP (Open Telecom Platfom) - това е платформа, която вече не се използва само за писане на телеком програми, а на софтуер, който има същите изисквания. Както споменахме по-горе, такива всъщност са доста от приложенията в ерата на интернет. По време на курса ще разгледаме част от библиотеките, които OTP предоставя. Засега повече информация може да намерите [тук](https://learnyousomeerlang.com/what-is-otp#its-the-open-telecom-platform). 69 | 70 | Грешно е да смятаме Elixir за конкурент, който ще убие Erlang. Напротив, Elixir е едно от най-хубавите неща, които можеше да му се случат. Erlang, който е неговата солидна основа, търпи все по-бурно развитие в следствие на интереса към Elixir и прииждането на нови хора в общността. 71 | 72 | Но за да се появи Elixir и да правим курс за него, а не за Erlang, то той трябва да поставя нещо ново на масата. 73 | 74 | - Elixir е Erlang. Всичко, което е плюс на Erlang, е плюс и на Elixir. 75 | - Но Elixir е и нещо повече. 76 | - По-добри инструменти в сравнение с Erlang. 77 | - Хубава документация. Създателят на езика твърди, че грешки в документацията трябва да се третират като грешки в самата програма. 78 | - Зад езика и неговата екосистема стоят опитни, отзивчиви и интелигентни хора. 79 | - Удобен и красив синтаксис. Erlang има минималистичен синтаксис, дори прекалено. Това води до доста boilerplate и повторения в кода. Липсата на макроси подчертава този проблем. 80 | - Мощни макроси. За разлика от тези в С/С++, макросите в Elixir не работят върху низове, ами върху AST (Abstract Syntax Tree). Макросите в Elixir са повече повлияни от тези в Lisp и Clojure. 81 | - Полиморфизъм чрез протоколи. 82 | - Качествени библиотеки. Доста библиотеки са написани на Erlang и са използвани дълго време, за да твърдим, че са стабилни и добре тествани. Но също така има доста библиотеки, написани сравнително наскоро на Elixir. Това има предимството много от грешките, направени в по-стари библиотеки в други езици да бъдат избегнати. Примери за такива нови библиотеки са: [Plug](https://hexdocs.pm/plug/readme.html), [Ecto](https://hexdocs.pm/ecto/Ecto.html), [Phoenix](http://phoenixframework.org), [GenStage](https://github.com/elixir-lang/gen_stage) и много други. 83 | - Pipe оператор `|>`. С негова помощ кодът е безспорно по-красив, удобен, четим, лесен за писане и за промяна. Едно скрито предимство на това е консистентността, която той вкарва в стандартната библиотека на езика. За да e по-лесно използването на `|>`, е прието функциите да приемат като първи аргумент данните, върху които работят. 84 | 85 | Нека разгледаме един пример за разликата между код, който не използва `|>` и код, който използва `|>`. 86 | 87 | Целта е да извършим поредица от трансформации върху списък от числа. 88 | 89 | Един начин да напишем това е: 90 | 91 | ```elixir 92 | data = [1,2,3,4,5] 93 | data_squared = Enum.map(data, fn n -> n*n end) 94 | data_filtered = Enum.filter(data_squared, fn n -> n >10 end) 95 | sum = Enum.reduce(data_filtered, 1, &(&1+&2)) 96 | ``` 97 | 98 | Ами ако не искаме да използваме толкова много временни променливи? Можем да вложим функциите: 99 | 100 | ```elixir 101 | Enum.reduce(Enum.filter(Enum.map([1,2,3,4,5], fn n -> n*n end),fn n ->n > 10 end), 1, &(&1 + &2)) 102 | ``` 103 | 104 | В този случай четимостта клони към нула, а добавянето на нова трансформация не е никак лека задача. 105 | 106 | Тук идва на помощ `|>`. Той подава резултата от лявата си страна като първи аргумент на израза в дясната си страна. Нищо повече, нищо по-малко, но достатъчно, за да можем да напишем нашия код по следния начин: 107 | 108 | ```elixir 109 | result = 110 | [1, 2, 3, 4, 5] 111 | |> Enum.map(fn n -> n * n end) 112 | |> Enum.filter(fn n -> n > 10 end) 113 | |> Enum.reduce(1, &(&1 + &2)) 114 | ``` 115 | 116 | ### Ами лошите страни на Erlang/Elixir? 117 | 118 | За да не се изреждат само суперлативи за езика, е редно да споменем и някои от не толкова хубавите му страни. Някои от тях са в този списък само защото изглеждат странно на пръв поглед, но всъщност са полезни и с течение на времето ще разберем защо. 119 | 120 | - Липсата на библиотеки, налични в много останали езици. Тъй като Elixir е нов език, а Erlang няма популярността на Java/Python/Ruby, то понякога ще се случва да няма библиотеката, която очаквате. Понякога ще се случва да има единствено Erlang библиотека, която без проблеми може да използвате, но ще трябва да научите и малко Erlang, ако искате да четете кода ѝ. 121 | - Заблудата, породена от изказвания на хора, незапознати с езика. Често срещано оплакване е, че синтаксисът около конкурентността не е толкова прост и минимален, както в Go. Това е така, защото конкурентността в Elixir решава проблеми, свързани с high availability, докато в Go се използва чисто и просто за конкурентността. 122 | - На пръв поглед не е очевидно къде се намират някои функции. Ако искате да намерите дължината на списък, то функцията в модула `List` ли се намира? Всъщност откривате, че няма нито `List.size`, нито `List.length`. Дължината се намира с `Enum.count`, която вътрешно използва `:erlang.length` (достъпна и като `Kernel.length` или просто `length`). Но с `Enum.count` можем да намерим и броя елементи на всичко, което имплементира протокола `Enumerable`. 123 | 124 | Проблемът с библиотеките е проблем на всеки език на този етап от развитието си. Нека уточним: тук не говорим за важни библиотеки - HTTP сървър/клиент, библиотеки за работа с бази данни, уеб фреймуърк, JSON декодери; такива библиотеки има, и то доста добри. Става въпрос за малките неща. Това не е проблем на език или платформата и е поправим, макар и бавно. На курса по Elixir във ФМИ ще даваме бонус точки за принос към някоя библиотека с отворен код. Още по-добре, ако създадете нова. 125 | 126 | ### Източници: 127 | 128 | [1] Цветинов, Николай (Meddle). Функционално програмиране с Elixir. Gitpich, 18.02.2019. Available from: https://gitpitch.com/ElixirCourse/presentations-2019/?p=welcome [cited 22.02.2019]. 129 | 130 | [2] Williams, Mike. The True story about why we invented Erlang and A few things you don’t want to tell your Manager. Erlang Factory, 26.02.2011. Available from: https://www.erlang-factory.com/upload/presentations/416/MikeWilliams.pdf [cited 22.02.2019]. 131 | 132 | [3] Hebert, Fred. The zen of Erlang. Ferd, 08.02.2016. Available from: https://ferd.ca/the-zen-of-erlang.html [cited 22.02.2019]. 133 | 134 | [4] Virding, Robert. Hitchhiker’s Tour of the BEAM. Erlang Factory, 2012. Available from: http://www.erlang-factory.com/upload/presentations/708/HitchhikersTouroftheBEAM.pdf [cited 22.02.2019]. 135 | 136 | Jurić, Saša. ElixirDaze 2017- Solid Ground by Saša Juric. YouTube, 16.03.2017. Availalble from: https://www.youtube.com/watch?v=5SbWapbXhKo [cited 22.02.2019]. 137 | 138 | [Официален сайт на Erlang](http://www.erlang.org/) 139 | 140 | [Официален сайт на Elixir](https://elixir-lang.org/) 141 | -------------------------------------------------------------------------------- /posts/news/first_homework.md: -------------------------------------------------------------------------------- 1 | --- 2 | category: Ресурси 3 | created_at: 2023-03-06T20:00:00 4 | pinned: true 5 | tags: 6 | - elixir 7 | - homework 8 | - github 9 | --- 10 | 11 | # Първо домашно 12 | 13 | Първото домашно на курса "Функционално програмиране с Elixir" може да получите от [този Github Classroom линк](https://classroom.github.com/a/UjvRT554). 14 | За да може да получите и предадете домашно, трябва да имате профил в [Github](https://github.com) 15 | 16 | Инструкции за работа с Github и предаване на домашното може да получите [на този линк](https://elixir-lang.bg/materials/posts/how_to_git). 17 | 18 | Кодът, който се намира в `main` клона при изтичане на крайния срок е кодът, който ще бъде оценяван. 19 | 20 | ### Краен срок 21 | 22 | Крайният срок за предаване на домашното е **24.03.2023 г. 23:59 ч.**. След изтичането на този срок правата Ви за писане в хранилището биват автоматично отнети. -------------------------------------------------------------------------------- /posts/news/fourth_homework.md: -------------------------------------------------------------------------------- 1 | --- 2 | category: Ресурси 3 | created_at: 2023-05-07T17:30:00 4 | pinned: true 5 | tags: 6 | - elixir 7 | - homework 8 | - github 9 | --- 10 | 11 | # Четвърто домашно 12 | 13 | Третото домашно на курса "Функционално програмиране с Elixir" може да получите от [този Github Classroom линк](https://classroom.github.com/a/P6she1aY). 14 | За да може да получите и предадете домашно, трябва да имате профил в [Github](https://github.com) 15 | 16 | Инструкции за работа с Github и предаване на домашното може да получите [на този линк](https://elixir-lang.bg/materials/posts/how_to_git). 17 | 18 | Кодът, който се намира в `main` клона при изтичане на крайния срок е кодът, който ще бъде оценяван. 19 | 20 | ### Разяснения 21 | 22 | Моля следете Discord сървъра на курса. В него могат да бъдат бъдат публикувани разяснения по домашното или някои поправки по условието, ако се открие грешка. 23 | 24 | Линк към сървъра може да намерите тук: https://elixir-lang.bg/posts/summary 25 | 26 | ### Краен срок 27 | 28 | Крайният срок за предаване на домашното е **04.06.2023 г. 23:59 ч.**. След изтичането на този срок правата Ви за писане в хранилището биват автоматично отнети. 29 | -------------------------------------------------------------------------------- /posts/news/second_homework.md: -------------------------------------------------------------------------------- 1 | --- 2 | category: Ресурси 3 | created_at: 2023-03-24T20:00:00 4 | pinned: true 5 | tags: 6 | - elixir 7 | - homework 8 | - github 9 | --- 10 | 11 | # Второ домашно 12 | 13 | Второто домашно на курса "Функционално програмиране с Elixir" може да получите от [този Github Classroom линк](https://classroom.github.com/a/v93OT8tS). 14 | За да може да получите и предадете домашно, трябва да имате профил в [Github](https://github.com) 15 | 16 | Инструкции за работа с Github и предаване на домашното може да получите [на този линк](https://elixir-lang.bg/materials/posts/how_to_git). 17 | 18 | Кодът, който се намира в `main` клона при изтичане на крайния срок е кодът, който ще бъде оценяван. 19 | 20 | ### Краен срок 21 | 22 | Крайният срок за предаване на домашното е **12.04.2023 г. 23:59 ч.**. След изтичането на този срок правата Ви за писане в хранилището биват автоматично отнети. 23 | -------------------------------------------------------------------------------- /posts/news/summary.md: -------------------------------------------------------------------------------- 1 | --- 2 | category: Ресурси 3 | created_at: 2023-02-22T08:00:00 4 | updated_at: 2023-03-29T22:00:00 5 | pinned: true 6 | author: Team 7 | tags: 8 | - elixir 9 | - resources 10 | - facebook 11 | - twitter 12 | - email 13 | - github 14 | --- 15 | 16 | # Обобщение на курса 17 | 18 | Тук можете да намерите обща информация за курса. 19 | 20 | 21 | ## Ресурси 22 | 23 | Линк към блога ни: 24 | 25 | Линк към Facebook групата ни: 26 | 27 | Линк към GitHub организацията ни: 28 | 29 | Можете да задавате въпроси на имейла: [course@elixir-lang.bg](mailto:course@elixir-lang.bg) 30 | 31 | Имаме Discord Server: 32 | 33 | ## Провеждане 34 | 35 | - Първата лекция на курса Функционално програмиране с Elixir ще се проведе в сряда, 22.02.2023 г., от 19:00 ч. в зала 200. 36 | - Занятията ще се провеждат всеки понеделник (зала 101) и сряда (зала 200) от 19:00 до 21:00. 37 | 38 | ## Оценяване 39 | 40 | - Оценката се образува от домашни през семестъра и защита на проект през сесията. 41 | - Използваме Github Classroom за предаване на домашни и проекти. 42 | - Няма тестове, писмен или теоретичен изпит. 43 | 44 | ## Теми 45 | 46 | ### 1. Защо Elixir? 47 | 48 | - [презентация](https://slides.elixir-lang.bg/slides/why_elixir.html) 49 | - [публикация](https://elixir-lang.bg/materials/posts/why_elixir) 50 | - [Livebook код](https://slides.elixir-lang.bg/slides/why_elixir.livemd) 51 | 52 | 53 | ### 2. Модули, функции и Mix 54 | 55 | - [презентация](https://slides.elixir-lang.bg/slides/modules_functions.html) 56 | - [публикация - Модули и функции](https://elixir-lang.bg/materials/posts/modules_and_functions) 57 | - [публикация - Инструментът Mix](https://elixir-lang.bg/materials/posts/mix_tool) 58 | - [Livebook код](https://slides.elixir-lang.bg/slides/modules_functions.livemd) 59 | 60 | 61 | ### 3. Tипове и основни конструкции 62 | 63 | - [презентация](https://slides.elixir-lang.bg/slides/types_pattern_matching_and_more.html) 64 | - [публикация - Основни типове](https://elixir-lang.bg/archive/posts/pattern_matching_types_and_basics) 65 | - [публикация - Съпоставяне на образци и управляващи оператори](https://elixir-lang.bg/materials/posts/pattern_matching_and_control_flow) 66 | - [публикация - Кортежи и списъци от ключове и стойности](https://elixir-lang.bg/materials/posts/tuples_and_keyword_lists) 67 | - [публикация - Списъци и потоци](https://elixir-lang.bg/materials/posts/enum_and_stream) 68 | - [Livebook код](https://slides.elixir-lang.bg/slides/types_pattern_matching_and_more.livemd) 69 | 70 | 71 | ### 4. Структури и протоколи 72 | 73 | - [презентация](https://slides.elixir-lang.bg/slides/structs_protocols.html) 74 | - [публикация - Речници](https://elixir-lang.bg/materials/posts/maps) 75 | - [публикация - Структури](https://elixir-lang.bg/materials/posts/structs) 76 | - [публикация - Протоколи](https://elixir-lang.bg/materials/posts/protocols) 77 | - [Livebook код](https://slides.elixir-lang.bg/slides/structs_protocols.livemd) 78 | 79 | ### 5. Низове и binaries 80 | 81 | - [презентация](https://slides.elixir-lang.bg/slides/binaries_and_strings.html) 82 | - [публикация - Binaries](https://elixir-lang.bg/materials/posts/binaries) 83 | - [публикация - Низове](https://elixir-lang.bg/materials/posts/strings) 84 | - [Livebook код](https://slides.elixir-lang.bg/slides/binaries_and_strings.livemd) 85 | 86 | ### 6. Увод в конкурентно програмиране с Elixir. Процеси, комуникация между процеси и връзки между процеси 87 | 88 | - [презентация](https://slides.elixir-lang.bg/slides/processes.html) 89 | - [публикация - Процеси](https://elixir-lang.bg/archive/posts/processes) 90 | - [публикация - Процеси и състояние](https://elixir-lang.bg/archive/posts/processes_and_state) 91 | - [публикация - Устройство и комуникация между процеси](https://elixir-lang.bg/archive/posts/process_internals) 92 | - [публикация - Връзки между процеси](https://elixir-lang.bg/archive/posts/process_links) 93 | - [Livebook код](https://slides.elixir-lang.bg/slides/processes.livemd) 94 | 95 | ### 7. Конкуректно програмиране с Elixir. Task и Agent 96 | 97 | - [презентация](https://slides.elixir-lang.bg/slides/agents_and_tasks.html) 98 | - [публикация - Task и Agent](https://elixir-lang.bg/materials/posts/concurrency_tasks_agents) 99 | 100 | ### 8. Конкурентно програмиране с Elixir. Поведението GenServer 101 | 102 | - [презентация](https://slides.elixir-lang.bg/slides/genserver.html) 103 | - [публикация - GenServer](https://elixir-lang.bg/materials/posts/gen_server) 104 | - [Livebook код](https://slides.elixir-lang.bg/slides/genserver.livemd) 105 | - [FileBuffer демо код](https://github.com/ElixirCourse/code2023/tree/master/file_buffer) 106 | 107 | ### 9. Конкуретно прогрмиране с Elixir. Поведенията Supervisor и Application 108 | 109 | - [презентация](https://slides.elixir-lang.bg/slides/supervisors_and_applications.html) 110 | 111 | ### 10. Разглеждане на проект с GenServer, Supervisor и Application 112 | 113 | - Няма презентация. 114 | - Лекцията протече под формата на разглеждане на реализиран малък проект и дискусия върху него с присъстващите. 115 | - [код](https://github.com/ElixirCourse/code2023/tree/master/asynch) 116 | 117 | ### 11. Грешки, работа с грешки и IO 118 | 119 | - [презентация](https://slides.elixir-lang.bg/slides/exceptions_io.html) 120 | - [Livebook код](https://slides.elixir-lang.bg/slides/ets.livemd) 121 | - [публикация - грешки](https://elixir-lang.bg/archive/posts/exceptions) 122 | - [публикация - IO](https://elixir-lang.bg/archive/posts/input_output) 123 | - [код](https://github.com/ElixirCourse/code2023/tree/master/ecio) 124 | - [код](https://github.com/ElixirCourse/code2023/tree/master/simple_logger) 125 | 126 | ### 12 и 13. Метапрограмиране I. Метапрограмиране II 127 | 128 | - [презентация](https://slides.elixir-lang.bg/slides/metaprogramming.html) 129 | - [публикация](https://elixir-lang.bg/materials/posts/metaprogramming_part1) 130 | - [публикация](https://elixir-lang.bg/materials/posts/metaprogramming_part2) 131 | 132 | ### 14. ETS 133 | 134 | - [презентация](https://slides.elixir-lang.bg/slides/ets.html) 135 | - [Livebook код](https://slides.elixir-lang.bg/slides/ets.livemd) 136 | 137 | ### 15. Дистрибутиран Elixir I 138 | 139 | - [презентация](https://slides.elixir-lang.bg/slides/distributed_elixir.html) 140 | - [Livebook код](https://slides.elixir-lang.bg/slides/distributed_elixir.livemd) 141 | -------------------------------------------------------------------------------- /posts/news/third_homework.md: -------------------------------------------------------------------------------- 1 | --- 2 | category: Ресурси 3 | created_at: 2023-04-23T14:00:00 4 | pinned: true 5 | tags: 6 | - elixir 7 | - homework 8 | - github 9 | --- 10 | 11 | # Трето домашно 12 | 13 | Третото домашно на курса "Функционално програмиране с Elixir" може да получите от [този Github Classroom линк](https://classroom.github.com/a/4NUStobB). 14 | За да може да получите и предадете домашно, трябва да имате профил в [Github](https://github.com) 15 | 16 | Инструкции за работа с Github и предаване на домашното може да получите [на този линк](https://elixir-lang.bg/materials/posts/how_to_git). 17 | 18 | Кодът, който се намира в `main` клона при изтичане на крайния срок е кодът, който ще бъде оценяван. 19 | 20 | ### Разяснения 21 | 22 | Моля следете Discord сървъра на курса. В него могат да бъдат бъдат публикувани разяснения по домашното или някои поправки по условието, ако се открие грешка. 23 | 24 | Линк към сървъра може да намерите тук: https://elixir-lang.bg/posts/summary 25 | 26 | ### Краен срок 27 | 28 | Времето за написване на домашното е 1 месец. Крайният срок за предаване на домашното е **23.05.2023 г. 23:59 ч.**. След изтичането на този срок правата Ви за писане в хранилището биват автоматично отнети. 29 | --------------------------------------------------------------------------------