Типове и основни конструкции

Image-Absolute

Курс по Elixir 2023, ФМИ

Да си преговорим!

  • Правим си проекти с какво?
  • Как пишем unit тестове?
  • Програмите ни са?
  • Четерите слоя на езика са?
  • Трябва да можем да боравим с git!
Курс по Elixir 2023, ФМИ

Основни типове

Image-Absolute

Курс по Elixir 2023, ФМИ

Типове и основни конструкции

Числа

1
10_000

0x53 # В шестнадесетична
0o53  # В осмична
0b11 # В двоична
3.14 # С плаваща запетая
1.0e-10
Курс по Elixir 2023, ФМИ
1 + 41
21 * 2
54 / 6 # Връща резултат с плаваща запетая
Kernel.div(54, 6) # В повечето езици `/` прави това
Kernel.rem 11, 3 # А ето как получаваме остатъка.
Курс по Elixir 2023, ФМИ
1 < 2
1 >= 1
1 != 2
1 == 2
1 == 1.0 # Операторът == сравнява по стойност
1 === 1.0 # Операторът === сравнява по стойност И тип
1 !== 1.0 # Операторът !== сравнява по стойност И тип
Курс по Elixir 2023, ФМИ

Булеви стойности: true/false

true
false
Kernel.is_boolean(true)
is_boolean(0)
true == false
Курс по Elixir 2023, ФМИ

Атоми

  • Атомите са константи, чието име е стойността им.
  • Булевите стойности true и false всъщност са атомите :true и :false
  • Имената на модули (колекции от функции и нещо повече) в Elixir също са атоми.
  • Модули идващи от Erlang са реферирани от атоми.
  • GC не ги събира.
Курс по Elixir 2023, ФМИ

Атоми

  • Удобни са за ползване като ключове в map-ове.
  • Задължителна част от keyword lists.
  • Често се използват в tuple-и за означаване на резултат от функция. Пример - {:ok, 2}
Курс по Elixir 2023, ФМИ

Атоми

  • Освен ако не са в двойни кавички, атомите могат да съдържат подчертавки, цифри и латински букви, както и at(@).
  • Атомите могат да завършват на ! или на ?.
  • Идеални за pattern matching (съпоставянето им е еквивалентно на съпоставяне на числа)
Курс по Elixir 2023, ФМИ

Атоми

:atom
:true
SomeModule # Може и да не е дефиниран
is_atom(:atom)
is_atom(true)
true == :true
:"atom with a space" # Могат да се дефинират и така
Курс по Elixir 2023, ФМИ

Низове

Низовете в Elixir се дефинират с двойни кавички и са с UTF-8 encoding:

Курс по Elixir 2023, ФМИ

Низове

"Здрасти"
"Здрасти #{:Danchou}" # Интерполация
Курс по Elixir 2023, ФМИ
is_binary("Здрасти")
String.length("Здрасти") # Брой на символи
byte_size("Здрасти") # Брой на байтове
"Бял" <> " мерцедес!" # Конкатенация
Курс по Elixir 2023, ФМИ

Списъци

  • Има специален модул, List, за работа с тях.
  • Не държат стойностите си подредени в паметта.
  • Намирането на дължината им, четене на стойност по index, добавяне на стойност на index и триене на стойност на index са все линейни операции.
Курс по Elixir 2023, ФМИ

Списъци

[1, 2, "три", 4.0] # Не са хомогенни
length [1, 2, 3, 5, 8] # Дължината
hd [1, 2, 3, 5, 8]
tl [1, 2, 3, 5, 8]
is_list([1, 2])
Курс по Elixir 2023, ФМИ

Списъци

  • Списък може да се представи рекурсивно като главата на списъка (head), в която е стойността на първия му елемент и указател към следващия елемент.
  • Тази дефиниция ни дава възможност да пишем функции, които работят със списъци.
Курс по Elixir 2023, ФМИ
[1 | [2 | [3 | [4 | [5 | []]]]]]
Курс по Elixir 2023, ФМИ

Списъци от символи

[83, 79, 83]

is_list('SOS')
Курс по Elixir 2023, ФМИ
List.to_atom([83, 79, 83])
List.to_atom([83, 79, 83, 1])
List.to_atom([83, 79, 83, 1, :s])

# List.to_integer/1 и List.to_float/1 ще опитат да върнат числа от charlist:

List.to_integer('54')
List.to_float('45.2')

List.to_string([83, 77, 69, 82, 67, 72])
Курс по Elixir 2023, ФМИ

Кортежи

Image-Absolute

Курс по Elixir 2023, ФМИ

Кортежи

  • Кортеж ни е официалният превод от "tuple".
  • Кортежите съхраняват елементите си подредени един след друг в паметта.
  • Достъпът до елемент по индекс и взимането на дължината им са константни операции.
Курс по Elixir 2023, ФМИ

Кортежи

  • Заедно с атомите за връщане на множество стойности от функция.
  • За pattern matching - ще видим малко по-долу.
  • Read-only колекция, защото писането в тях е скъпа операция.
Курс по Elixir 2023, ФМИ

Кортежи

{:ok, 7}
tuple_size({:ok, 7, 5})
is_tuple({:ok, 7, 5})
Курс по Elixir 2023, ФМИ

Keyword lists

Списъци, които съдържат tuple-и от по два елемента - атом и каквато и да е стойност.

Курс по Elixir 2023, ФМИ

Keyword lists

[{:one, 1}, {:two, 2}]
[one: 1, two: 2]
Курс по Elixir 2023, ФМИ

Ако keyword list е последен аргумент на функция, можем да пропуснем квадратните скоби при извикване:

f(1, 2, three: 3, four: 4)
Курс по Elixir 2023, ФМИ

Keyword lists

  • Ключовете им могат да се повтарят.
  • Използват се и за предаване на command line параметри или опции на функции.
  • Пример е String.split/3.
Курс по Elixir 2023, ФМИ
String.split("one,two,,,three,,,four", ",", trim: true)
Курс по Elixir 2023, ФМИ

Maps

  • Колекции от ключове и стойности.
  • Map-овете в Elixir не позволяват еднакви ключове.
  • За ключове може да се използва всичко и дори няма нужда да бъдат един и същи тип,
    но обикновено се използват низове или атоми.
Курс по Elixir 2023, ФМИ

Maps

pesho = %{
  name: "Пешо",
  age: 35,
  hobbies: {:drink, :smoke, :eurofootball},
  job: "шлосер"
}

pesho[:name]
pesho.name
Map.fetch(pesho, :name)
Map.fetch!(pesho, :name)
Map.get(pesho, :name)
Курс по Elixir 2023, ФМИ

Бинарен тип (Binaries)

  • Представляват поредици от битове и байтове.
Курс по Elixir 2023, ФМИ

Бинарен тип (Binaries)

<< 2 >> # Цялото число 2 в 1 байт
byte_size << 2 >>
<< 255 >> # Цялото число 255 в 1 байт
<< 256 >> # Превърта и става 0
<<1, 2>> # Две цели числа в два байта.
byte_size << 1, 2 >>
Курс по Elixir 2023, ФМИ
<< 5::size(3), 1::size(1), 5::size(4) >>
0b10110101
byte_size << 5::size(3), 1::size(1), 5::size(4) >>
is_bitstring << 5::size(3), 1::size(1) >>
is_binary << 5::size(3), 1::size(1) >>
Курс по Elixir 2023, ФМИ

Бинарен тип (Binaries)

  • Интересен факт - низовете в Elixir са имплементирани като binary тип.
  • Спомняте си че is_binary("Стринг") връщаше true.
<<208, 170, 208, 156>> = "ЪМ"
Курс по Elixir 2023, ФМИ

Анонимни функции

fn (x) -> x + 1 end
(fn (x) -> x + 1 end).(4) # Извикване
is_function((fn (x) -> x + 1 end))
Курс по Elixir 2023, ФМИ
&(&1 + 1)
(&(&1 + 1)).(4)
Курс по Elixir 2023, ФМИ

Анонимни функции

  • Анонимните функции са си стойности, могат да се подават на функции и да се връщат.
  • Модулни функции могат да се подават като стойности също.
  • Функцията е винаги израз, няма return, каквото последно е в тялото ѝ е резултатът ѝ.
Курс по Elixir 2023, ФМИ

Други типове

  • Други типове са Port, Reference и PID, които се използват с процеси.
  • Има и регулярни изрази. ~r/\w+/im
  • Ranges : (1..1000)
  • Има различни shortcut-синтаксиси за дефиниране на някои от типовете.
Курс по Elixir 2023, ФМИ

Съпоставяне на образци

Image-Absolute

Курс по Elixir 2023, ФМИ

Съпоставяне на образци

  • В Elixir pattern matching-ът е еднa от най-важните и основни особености.
  • Операторът = се нарича match operator.
  • Можем да го сравним с знака = в математиката.
Курс по Elixir 2023, ФМИ

Съпоставяне на образци

  • Използвайки го, превръщаме целия израз в уравнение, в което сравняваме лявата с дясната страна.
  • Ако сравнението е успешно се връща стойността на това уравнение, ако не - има грешка.
Курс по Elixir 2023, ФМИ

Съпоставяне на образци

x = 5
5 = x
4 = x
Курс по Elixir 2023, ФМИ

Засега за match operator-а знаем:

  1. С него могат да се дефинират променливи.
  2. С него могат да се правят проверки - дали дадена променлива има дадена стойност.
Курс по Elixir 2023, ФМИ

Съпоставяне на образци

  • Имената на променливи задължително започват с малка латинска буква или подчертавка (_),
    следвана от букви, цифри или подчертавки.
  • Могат да завършват на ? или !.
  • Операторът = ще опита да присвои на всички възможни променливи от ляво стойности от дясно.
Курс по Elixir 2023, ФМИ
{one, tWo, t3, f_our, five!} = {1, 2, 3, 4, 5}
one
tWo
t3
f_our
five!
Курс по Elixir 2023, ФМИ
g = fn
  0 -> 0
  x -> x - 1
end
g.(0)
g.(3)
Курс по Elixir 2023, ФМИ

Image-Absolute

Курс по Elixir 2023, ФМИ

Съпоставяне на образци (pin)

  • В Elixir e възможно да променим стойността на променлива (не точно, но shadows!).
  • В Erlang това не е възможно.
Курс по Elixir 2023, ФМИ

Ако искаме една променлива, която вече съществува да не промени стойността си при съпоставяне,
можем да използваме pin оператора - ^.

x = 5
^x = 6
Курс по Elixir 2023, ФМИ
{y, ^x} = {5, 4}
Курс по Elixir 2023, ФМИ

Ако се опитаме да присвоим стойност на unbound променлива (досега не е съществувала),
използвайки pin оператора, ще получим грешка.

^z = 4
Курс по Elixir 2023, ФМИ

Параметри на функции

defmodule Example do
  def factorial(0), do: 1
  def factorial(n), do: n * factorial(n - 1)
end
Курс по Elixir 2023, ФМИ

Гардове

defmodule Questionnaire do
  def how_old_are_you?(age) when age > 30 do
    "Имаме си чичко или леличка"
  end
  def how_old_are_you?(age) when age > 20 do
    "Имаме си милениалче, тригърнато от живота"
  end
  def how_old_are_you?(_), do: "Дете"
end

Questionnaire.how_old_are_you?(21)
Курс по Elixir 2023, ФМИ

Списъци

[a | b] = [1, 2, 3]

[a | b] = [25]
Курс по Elixir 2023, ФМИ
[a | b] = []
Курс по Elixir 2023, ФМИ
defmodule ListUtils do
  def length([]), do: 0
  def length([_head | tail]), do: 1 + length(tail)
end

ListUtils.length(["cat", "dog", "fish", "horse"])
Курс по Elixir 2023, ФМИ
defmodule Square do
  def of([]), do: []
  def of([head | tail]), do: [head * head | of(tail)]
end

Square.of([1, 2, 3, 4, 5])
Курс по Elixir 2023, ФМИ

Фибоначи

defmodule Fib do
  def bad_of(n), do: bad_fib(n, 0, 1, [])

  defp bad_fib(0, _current, _next, seq), do: seq

  defp bad_fib(n, current, next, seq) do
    bad_fib(n - 1, next, current + next, seq ++ [current])
  end
end
Курс по Elixir 2023, ФМИ
defmodule Fib do
  def of(n), do: fib(n, 0, 1, [])

  defp fib(0, _current, _next, seq), do: seq |> Enum.reverse()

  defp fib(n, current, next, seq) do
    fib(n - 1, next, current + next, [current | seq])
  end
end
Курс по Elixir 2023, ФМИ

map/reduce

defmodule ListUtils do
  def map([], _func), do: []
  def map([head | tail], func), do: [func.(head) | map(tail, func)]
end

ListUtils.map([1, 2, 3, 4, 5], fn x -> x * x end)
Курс по Elixir 2023, ФМИ
defmodule ListUtils do
  def reduce([], acc, _func), do: acc
  def reduce([head | tail], acc, func), do: reduce(tail, func.(head, acc), func)
end

ListUtils.reduce(["cat", "dog", "horse"], 0, fn _head, acc -> 1 + acc end)
Курс по Elixir 2023, ФМИ

Речници

pesho = %{age: 35, drink: :rakia, hobbies: :just_drinking, name: "Пешо"}

%{name: x} = pesho

%{name: _, age: _} = pesho
Курс по Elixir 2023, ФМИ
defmodule A do
  def f(%{name: name} = person) do
    name
  end
end

A.f(pesho)
Курс по Elixir 2023, ФМИ

Control Flow

Image-Absolute

Курс по Elixir 2023, ФМИ

Control Flow

a = :rand.uniform(1000)

if rem(a, 2) == 0 do
  "a's value is #{a}, it is even"
else
  "a's value is #{a}, it is even"
end
Курс по Elixir 2023, ФМИ

if / unless

defmodule Questionnaire do
  def how_old_are_you?(age) do
    if age > 30 do
      "Имаме си чичко или леличка"
    else
      if age > 20 do
        "Имаме си милениалче, тригърнато от живота"
      else
        "Дете"
      end
    end
  end
end
Курс по Elixir 2023, ФМИ

Стойността на if или на unless е стойността на израза, който се оценява.

age = 34
name = "Слави"

if age < 30 do
  "Младеж"
end

if age > 30, do: "Чичо #{name}", else: name
Курс по Elixir 2023, ФМИ

Cond

Image-Absolute

Курс по Elixir 2023, ФМИ

Cond

defmodule FizzBuzz do
  def of(n), do: Enum.map(1..n, &number_value/1)

  defp number_value(n) do
    cond do
      rem(n, 3) == 0 and rem(n, 5) == 0 -> "FizzBuzz"
      rem(n, 3) == 0 -> "Fizz"
      rem(n, 5) == 0 -> "Buzz"
      true -> n
    end
  end
end
Курс по Elixir 2023, ФМИ

Cond

  • В 'cond' слагаме списък от условия, със свързан с тях код.
  • Оценяват се докато стигнем до някое, което се оцени като true.
  • Когато това стане, се изпълнява асоциираният с него код и това е стойността на cond-a.
Курс по Elixir 2023, ФМИ

Cond

  • Обикновено последното условие на cond е 'true', за да има код, който да се изпълни,
  • Ако не го сложим и нищо не се оцени като истина ще има грешка.
Курс по Elixir 2023, ФМИ

Специални форми

  • За разлика от if, cond е 'специална форма'.
  • Специалните форми са най-базовите градивни единици в Elixir.
  • Не могат да се пренапишат от нас.
  • Те са специални macro-си, които не са на написани на езика.
Курс по Elixir 2023, ФМИ

Специални форми

  • Конструкциите if и unless са добавени за да се справи един императивен програмист с навлизането в Elixir.
  • Според нас са повече вредни за обучението, отколкото помагат.
  • Забравете че ги има.
Курс по Elixir 2023, ФМИ

Case

Image-Absolute

Курс по Elixir 2023, ФМИ

Case

defmodule Questionnaire do
  def asl(age, sex, location) do
    case sex do
      :male ->
        "#{age}, М, #{location}"
      :female ->
        "#{age}, F, #{location}"
      _ ->
        "#{age}, ?, #{location}"
    end
  end
end
Курс по Elixir 2023, ФМИ

Case

  • Нужна е последна клауза, която match-ва всякакви стойности.
  • Ако нищо не се match-не се изпълнява тази последна клауза.
  • Ако я няма ще имаме грешка (CaseClauseError).
Курс по Elixir 2023, ФМИ

Case

  • Важно е да знаем, че case, също като cond е специална форма.
  • Ползваме го само ако кодът ни стане прекалено разхвърлян или труден за разбиране с функции pattern matching.
  • Забравете за if и unless, по добре използвайте case.
Курс по Elixir 2023, ФМИ

Защо case е по-добър избор пред cond?

  • Проверките които правим в case, са свързани с data-та която му даваме.
  • Лесно можем да съобразим какво и защо проверяваме.
  • По-трудно да вкараме странични ефекти.
Курс по Elixir 2023, ФМИ

Case

  • Проверките в cond могат да са всякакви и да са свързани с всякакви данни.
  • Много по-лесно е да напишем код, в който се чудим кое от къде идва.
  • Много по-лесно е да имаме странични ефекти.
Курс по Elixir 2023, ФМИ

Конструкцията with

Image-Absolute

Курс по Elixir 2023, ФМИ

Конструкцията with

  • Още една специална форма, която опростява писането на код.
  • Това, което прави with е да комбинира множество успешни съпоставяния.
  • Ако искаме няколко различни условия да са изпълнени за да направим нещо и ако само едно от тези условия не е изпълнено, да не го правим.
Курс по Elixir 2023, ФМИ

Конструкцията with

defmodule HR do
  def work_experience(years) when years > 10, do: :experienced
  def work_experience(years) when years > 5, do: :advanced
  def work_experience(_), do: :not_experienced

  def knows_elixir?([]), do: false
  def knows_elixir?([:elixir | _]), do: true
  def knows_elixir?([_ | rest]), do: knows_elixir?(rest)

  def read_cv(file_path), do: File.read(file_path)
end
Курс по Elixir 2023, ФМИ
years = 11

languages = [:erlang, :elixir, :rust]

cv_path = "/tmp/cv.txt"

with :experienced <- HR.work_experience(years),
     true <- HR.knows_elixir?(languages),
     {:ok, cv} <- HR.read_cv(cv_path),
     do: cv
Курс по Elixir 2023, ФМИ

Ако някое от условията не е изпълнено, ще получим стойността му:

with :experienced <- HR.work_experience(3),
     true <- HR.knows_elixir?(languages),
     {:ok, cv} <- HR.read_cv(cv_path),
     do: cv
Курс по Elixir 2023, ФМИ

Конструкцията with

  • Можем да правим още неща с with - погледнете документацията
  • Погледнете и другите специални форми!
Курс по Elixir 2023, ФМИ

Неизменимост

Image-Absolute

Курс по Elixir 2023, ФМИ

Неизменимост

base_list = [1, 2, 3]
new_list = [0 | base_list]
Курс по Elixir 2023, ФМИ

Материали

Курс по Elixir 2023, ФМИ

Край

Image-Absolute

Курс по Elixir 2023, ФМИ