| theme | style | paginate | footer | backgroundImage | marp |
|---|---|---|---|---|---|
uncover |
.small-text {
font-size: 0.75rem;
}
code.language-elixir {
background: #000;
color: #f8f8f8;
}
section {
letter-spacing: 1px !important;
}
span.hljs-comment,
span.hljs-quote,
span.hljs-meta {
color: #7c7c7c;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-tag,
.hljs-name {
color: #f96a6a;
}
.hljs-attribute,
.hljs-selector-id {
color: #ffffb6;
}
.hljs-string,
.hljs-selector-attr,
.hljs-selector-pseudo,
.hljs-addition {
color: #a8ff60;
}
.hljs-subst {
color: #daefa3;
}
.hljs-regexp,
.hljs-link {
color: #e9c062;
}
.hljs-title,
.hljs-section,
.hljs-type,
.hljs-doctag {
color: #f0f05b;
}
.hljs-symbol,
.hljs-bullet,
.hljs-variable,
.hljs-template-variable,
.hljs-literal {
color: #ffc57a;
}
.hljs-number,
.hljs-deletion {
color:#ff73fd;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}
li {
font-size: 32px;
}
|
true |
Курс по Elixir 2023, ФМИ |
linear-gradient(to bottom, #E0EAFC, #CFDEF3) |
true |
- Как се създава речник (
Map)? - Какви могат да бъдат ключовете в един речник?
- Как досъпваме елемент на речник?
- Как променяме нещо в даден речник?
- Как можем да създадем речник подобен на вече съществуващ, но:
- с добавен ключ
- с премахнат ключ
- с променена стойност за даден ключ
- Как проверяваме дали нещо е речник?
- Как намираме размера на даден речник?
Една структура всъщност много прилича на Map, с няколко разлики:
- Ключовете ѝ са атоми.
- Задължително атоми!
- Ключовете ѝ са предварително дефинирани.
Дефиниране на структура:
- Структурите се дефинират в модул използвайки макроса
defstruct. - Структурата взема името на модула в който е дефинирана.
- Може да дaдадем стойности по-подразбиране на някoe поле на структурата.
- Може да направим някое поле задължително с модул атрибута
@enforce_keys.
defmodule Person do
@enforce_keys [:name]
defstruct [:name, :location, children: []]
endРазликите са:
- Траява да дадем името на структурата
pesho = %Person{
name: "Пешо",
location: "Некаде",
children: "Нема"
}Разликите са:
- Ако пропуснем да дадем стойност на даден дефиниран ключ, той ще получи стойността си по подразбиране
- Ако няма дефинирана стойност по подразбиране, то стойността му ще е
nil
pesho = %Person{ name: "Пешо" }Разликите са:
- Ако не дадем стойност на ключ, който е обявен за задължителен получаваме грешка
%Person{ children: "Пет или шес'" }Разликите са:
- Не можем да даваме ключове, които не са дефинирани в структурата
%Person{name: "Пешо", drinks: "ВодKа"}Нека пробваме няколко неща
pesho = %Person{
name: "Пешо",
children: "Нема",
location: "НикАде"
}
IO.inspect(pesho, label: "Pesho is")
IO.inspect(is_map(pesho), label: "Pesho is Map")
IO.inspect(map_size(pesho), label: "Pesho size is")Структурите всъщност са Map-ове със специалния ключ __struct__
IO.inspect(pesho, structs: false, label: "Pesho actually is")Хубава новина е, че Map модулът работи със структури.
Map.put(pesho, :name, "Стойчо")Операторът за обновяване също работи.
%{ pesho | children: "Жената ги брои" }Достъпът до елементите на структура със . също работи:
pesho.nameДостъп до елементите на структура с [] НЕ раобти:
pesho[:name]%{ age: x } = %{ name: "Гошо", age: 25 }
xpesho = %Person{name: "Пешо", children: "Нема", location: "НикАде"}%{children: x} = pesho
x%Person{location: x} = pesho
x%Person{name: x} = %{name: "Гошо"}
x%Person{name: x} = pesho%{__struct__: Person, name: x} = peshoГорните две два записа са еквивалентни.
- Това е дборе защото, така можем да проверяваме (асъртваме) дали нещо е "инстанция" на дадена структура
- Още по-добре компилатора може да асъртва дали нещо е "инстанция" на дадена структура
Пример
defmodule P do
def children_names(%Person{children: []}), do: "No children"
def children_names(%Person{children: [name]}), do: "My childe is called: #{name}"
def children_names(%Person{} = p) do
p.childern
|> Enum.join(", ")
|> then(&"The name of my children are: #{&1}")
end
endP.children_names(%Person{name: "Пешо", children: []})neo_pesho = %Person{name: "Пешо", children: ["Иванчо", "Драганчо"]}
P.children_names(neo_pesho)- Структура се дефинира в модул с идеята, че е нещо като тип дефиниран от нас.
- В модула обикновено слагаме функции, които да работят с този тип:
- такива които "конструира" инстанция на структрата по дадени аргументи
- или фунциим, които приемат инстанция на структурата, като първи акгумент
- Така имаме на едно място дефиницията на типа и функциите за работа с него.
Range
range = 3..93
IO.inspect(range, structs: false)Regex
regex = ~r("Red")
IO.inspect(regex, structs: false)MapSet
set = MapSet.new([2, 2, 3])
IO.inspect(set, structs: false)Time
{:ok, time} = Time.new(12, 34, 56)
IO.inspect(time, structs: false)Прости JSON типове:
- null (това е и стойността му)
- boolean (false или true)
- number (2, 5.5, -12 и др.)
- string ("Пешо")
Съставни JSON типове:
- array (нехомогенен списък от JSON типове)
- [1, null, [false, true]]
- object (мап с ключове стрингове и стойности произволен JSON тип)
- {"name": "Пешо", "children": ["Гошо", "Ташо"], "location" : {"country": "БAлгариА", "city": "Карнобат"}}
Всеки JSON тип е валиден JSON
Дефиниране на протокол:
- използваме макроса
defprotocol - в блок описваме сигнатурите на функциите от протокола
- можем да имплементираме протокола за произволен erlang ТИП или elixir СТРУКТУРА
defprotocol JSON do
@doc "Converts the given data to its JSON representation"
def encode(data)
endJSON.encode(nil)- Протокол се имплементира с макрото
defimpl. - Ето кака бихме имплементираме
JSONза атоми:
defimpl JSON, for: Atom do
def encode(true), do: "true"
def encode(false), do: "false"
def encode(nil), do: "null"
def encode(atom) do
JSON.encode(Atom.to_string(atom))
end
endJSON.encode(true)JSON.encode(false)JSON.encode(:name)defimpl JSON, for: BitString do
def encode(<< >>), do: ~s("") # <=> "\"\""
def encode(str) do
cond do
String.valid?(str) -> ~s("#{str}")
true -> str |> bitstring_to_list() |> JSON.encode()
end
end
# Измислена конвенция за кодиране на байтове и битов
defp bitstring_to_list(binary) when is_binary(binary) do
list_of_bytes(binary, [:bytes])
end
defp bitstring_to_list(bits), do: list_of_bits(bits, [:bits])
defp list_of_bytes(<<>>, list), do: list |> Enum.reverse()
defp list_of_bytes(<< x, rest::binary >>, list), do: list_of_bytes(rest, [x | list])
defp list_of_bits(<<>>, list), do: list |> Enum.reverse()
defp list_of_bits(<< x::1, rest::bits >>, list), do: list_of_bits(rest, [x | list])
endJSON.encode(:name)JSON.encode("")JSON.encode("some")JSON.encode(<< 200, 201 >>)defimpl JSON, for: List do
def encode(list) do
list
|> Enum.map(&JSON.encode/1)
|> Enum.join(", ")
|> then(&"[#{&1}]")
end
endJSON.encode([nil, true, false])JSON.encode(<< 200, 201 >>)defimpl JSON, for: Integer do
def encode(n), do: n
endJSON.encode(<< 200, 201 >>)defimpl JSON, for: Map do
def encode(map) do
map
|> Enum.map(&encode_pair/1)
|> Enum.join(", ")
|> then(&"{ #{&1} }")
end
defp encode_pair({key, value}) when is_binary(key) do
"#{JSON.encode(to_string(key))}: #{JSON.encode(value)}"
end
endfree_pesho = %{
name: "Pesho",
age: 43,
likes: [:drinking, "eating shopska salad", "да гледа мачове"]
}JSON.encode(free_pesho)defmodule Man do
defstruct [:name, :age, :likes]
endkosta = %Man{
name: "Коста",
age: 54,
likes: ["Турбо фолк", "Телевизия", "да гледа мачове"]
}JSON.encode(kosta)Вградените типове за които можем да имплементираме протокол са:
AtomBitStringFloatFunctionInteger
ListMapPIDPortReferenceTuple
Има и начин да имплементираме протокол за всички случаи за които не е имплементиран,
използвайки Any:
defimpl JSON, for: Any do
def encode(_), do: "null"
endНека да пробваме сега
JSON.encode(kosta)За да използваме имплементацията за Any трябва да упомененм, че го наследяваме с модулния атрибут @derive ProtocolName
defmodule Man2 do
@derive JSON
defstruct [:name, :age, :likes]
endnikodim = %Man2{
name: "Никодим", age: 15, likes: ["Да лежи", "GTA V"]
}
JSON.encode(nikodim)При дефиниране на протокол можен да използваме атрибута @fallback_to_any двайки му стойност true.
defprotocol JSON2 do
@fallback_to_any true
@doc "Converts the given data to its JSON representation"
def encode(data)
endТова означава, че всеки тип, който не имплементира протокола ще изпозва Any.
defimpl JSON2, for Any do
def encode(_data), do: "It's-a-Me, Any!"
end
JSON2.encode({:ok, :KO})path = :code.lib_dir(:elixir, :ebin)
Protocol.extract_protocols([path])Collectable- това е протоколът, използван отEnum.into.Inspect- използва се за pretty printing.String.Chars-Kernel.to_string/1го използва.List.Chars-Kernel.to_charlist/1го използва.Enumerable- функциите отEnumмодула очакват типове, за които е имплементиран, като първи аргумент.
Имплементатори на даден протокол можем да намерим така:
Protocol.extract_impls(Enumerable, [path])defimpl Enumerable, for: BitString do
def count(str), do: {:ok, String.length(str)}
def member?(str, char), do: {:ok, String.contains?(str, char)}
def reduce(_, {:halt, acc}, _fun), do: {:halted, acc}
def reduce(str, {:suspend, acc}, fun) do
{:suspended, acc, &reduce(str, &1, fun)}
end
def reduce("", {:cont, acc}, _fun), do: {:done, acc}
def reduce(str, {:cont, acc}, fun) do
{next, rest} = String.next_grapheme(str)
reduce(rest, fun.(next, acc), fun)
end
def slice(str), do: {:error, __MODULE__}
end"Еликсир"
|> Enum.filter(fn
c when c in ~w(а ъ о у е и) -> false
_ -> true
end)
|> Enum.join("")




