Bootstrap

Базовые типы данных elixir, и их неизменяемость

Базовые типы данных elixir, и их неизменяемость

НЕДЕЛЯ 1: Основы Elixir и функционального программирования

Тема 2: Базовые типы данных и неизменяемость (3 часа)

2.1 Базовые типы данных

Elixir имеет следующие базовые типы:

Integers (Целые числа)

Terminal:

  
# Десятичные
iex> 42
42

# Бинарные
iex> 0b1010
10

# Восьмеричные
iex> 0o777
511

# Шестнадцатеричные
iex> 0xFF
255

# Можно использовать underscore для читаемости
iex> 1_000_000
1000000

# Операции
iex> 10 + 5
15
iex> 10 - 5
5
iex> 10 * 5
50
iex> div(10, 3)    # Целочисленное деление
3
iex> rem(10, 3)    # Остаток от деления
1
    

Floats (Числа с плавающей точкой)

Terminal:

  
iex> 3.14
3.14

iex> 1.0e-10
1.0e-10

# Операции
iex> 10 / 3
3.3333333333333335

iex> Float.round(3.14159, 2)
3.14

iex> Float.ceil(3.14)
4.0

iex> Float.floor(3.14)
3.0
    

Важно: В Elixir оператор / всегда возвращает float!

Booleans (Булевы значения)

Terminal:

  
iex> true
true

iex> false
false

iex> is_boolean(true)
true

# Логические операции
iex> true and false
false

iex> true or false
true

iex> not true
false

# Операторы || && ! работают с любыми типами
iex> 1 || 2
1

iex> false || 42
42

iex> nil && 13
nil
    

Важно: В Elixir только false и nil считаются falsy. Всё остальное - truthy (включая 0 и пустые строки)!

Atoms (Атомы)

Атомы - это константы, имя которых является их значением.

Terminal:

  
iex> :apple
:apple

iex> :orange
:orange

iex> :error
:error

# true, false и nil - тоже атомы
iex> true == :true
true

iex> is_atom(false)
true

iex> is_boolean(:false)
true

# Атомы часто используются для тегирования значений
iex> {:ok, "Success"}
{:ok, "Success"}

iex> {:error, "Something went wrong"}
{:error, "Something went wrong"}
    

Применение атомов:

  • Ключи в maps
  • Статусы операций (:ok, :error)
  • Опции функций
  • Идентификаторы модулей

Strings (Строки)

Terminal:

  
# Строки в двойных кавычках - UTF-8 encoded binaries
iex> "hello"
"hello"

iex> "привет"
"привет"

iex> "hello
...> world"
"hello\nworld"

# Интерполяция
iex> name = "Elixir"
iex> "Hello, #{name}!"
"Hello, Elixir!"

# Конкатенация
iex> "Hello" <> " " <> "World"
"Hello World"

# Длина строки
iex> String.length("привет")
6

# Проверка типа
iex> is_binary("hello")
true

# Uppercase/Lowercase
iex> String.upcase("hello")
"HELLO"

iex> String.downcase("HELLO")
"hello"

# Разделение
iex> String.split("hello world", " ")
["hello", "world"]

# Обрезка пробелов
iex> String.trim("  hello  ")
"hello"

# Проверка на содержание
iex> String.contains?("hello world", "world")
true
    

Charlists - списки символов (редко используются):

Terminal:

  
iex> 'hello'
'hello'

iex> [104, 101, 108, 108, 111]
'hello'

iex> is_list('hello')
true
    

2.2 Составные типы данных

Tuples (Кортежи)

Кортежи хранят элементы в смежной памяти.

Terminal:

  
# Создание кортежа
iex> {:ok, "hello"}
{:ok, "hello"}

iex> tuple = {1, 2, 3}
{1, 2, 3}

# Доступ по индексу (с 0)
iex> elem(tuple, 1)
2

# Размер кортежа
iex> tuple_size(tuple)
3

# Изменение элемента (создаёт новый кортеж!)
iex> put_elem(tuple, 0, :a)
{:a, 2, 3}

# Оригинальный кортеж не изменился
iex> tuple
{1, 2, 3}
    

Когда использовать кортежи:

  • Фиксированное количество элементов
  • Возврат значений из функций
  • Pattern matching

Примеры использования:

Terminal:

  
# Возврат статуса и значения
{:ok, result} = perform_operation()

# RGB цвета
color = {255, 128, 0}

# Координаты
point = {10, 20}
    

Lists (Списки)

Списки - это связанные списки (linked lists).

Terminal:

  
# Создание списка
iex> [1, 2, 3]
[1, 2, 3]

iex> list = [1, 2, 3, 4, 5]

# Длина списка
iex> length(list)
5

# Конкатенация
iex> [1, 2] ++ [3, 4]
[1, 2, 3, 4]

# Вычитание
iex> [1, 2, 3, 4] -- [2, 4]
[1, 3]

# Голова и хвост
iex> hd([1, 2, 3])
1

iex> tl([1, 2, 3])
[2, 3]

# Добавление в начало (эффективная операция!)
iex> [0 | [1, 2, 3]]
[0, 1, 2, 3]

# Можно разделить список на голову и хвост
iex> [head | tail] = [1, 2, 3]
iex> head
1
iex> tail
[2, 3]
    

Важные особенности списков:

  • Доступ к голове: O(1)
  • Доступ по индексу: O(n)
  • Добавление в начало: O(1)
  • Добавление в конец: O(n)

Terminal:

  
# Проверка на вхождение
iex> 2 in [1, 2, 3]
true

# Списки могут содержать разные типы
iex> [1, :atom, "string", 3.14]
[1, :atom, "string", 3.14]

# Списки списков
iex> 1, 2], [3, 4], [5, 6
1, 2], [3, 4], [5, 6
    

Keyword Lists (Списки ключевых слов)

Специальный синтаксис для списков кортежей с атомами.

Terminal:

  
# Обычный синтаксис
iex> [{:a, 1}, {:b, 2}]
[a: 1, b: 2]

# Краткий синтаксис
iex> [a: 1, b: 2]
[a: 1, b: 2]

# Доступ к значениям
iex> list = [a: 1, b: 2]
iex> list[:a]
1

iex> list[:c]
nil

# Можно иметь дубликаты ключей
iex> [a: 1, a: 2]
[a: 1, a: 2]
    

Когда использовать keyword lists:

  • Опции функций
  • Запросы к базе данных
  • Когда порядок важен
  • Когда могут быть дубликаты ключей

Terminal:

  
# Типичное использование
def render(template, opts \\ []) do
  # opts может быть [title: "Page", layout: "main"]
end

# Вызов
render("index.html", title: "Home", layout: "app")
    

Maps (Словари)

Maps - это key-value структуры данных.

Terminal:

  
# Создание map
iex> %{"a" => 1, "b" => 2}
%{"a" => 1, "b" => 2}

iex> %{:a => 1, :b => 2}
%{a: 1, b: 2}

# Короткий синтаксис для atom ключей
iex> %{a: 1, b: 2}
%{a: 1, b: 2}

# Map может иметь ключи любого типа
iex> %{1 => "one", 2 => "two"}
%{1 => "one", 2 => "two"}

# Доступ к значениям
iex> map = %{a: 1, b: 2}
iex> map[:a]
1

iex> map.a
1

iex> Map.get(map, :c, "default")
"default"

# Обновление map (создаёт новый!)
iex> %{map | a: 3}
%{a: 3, b: 2}

# Добавление нового ключа
iex> Map.put(map, :c, 3)
%{a: 1, b: 2, c: 3}

# Удаление ключа
iex> Map.delete(map, :a)
%{b: 2}

# Проверка наличия ключа
iex> Map.has_key?(map, :a)
true

# Получить все ключи
iex> Map.keys(map)
[:a, :b]

# Получить все значения
iex> Map.values(map)
[1, 2]
    

Важно: Ключи в maps уникальны, в отличие от keyword lists!

Terminal:

  
# Вложенные maps
iex> user = %{
...>   name: "John",
...>   age: 30,
...>   address: %{
...>     city: "New York",
...>     country: "USA"
...>   }
...> }

# Доступ к вложенным значениям
iex> user.address.city
"New York"

# Обновление вложенных значений
iex> put_in(user.address.city, "Boston")
%{
  name: "John",
  age: 30,
  address: %{city: "Boston", country: "USA"}
}
    

2.3 Концепция неизменяемости (Immutability)

Главный принцип: В Elixir все данные неизменяемы!

Terminal:

  
# Пример
iex> list = [1, 2, 3]
iex> List.delete(list, 1)
[2, 3]

# Оригинальный список не изменился!
iex> list
[1, 2, 3]
    

Почему неизменяемость важна:

  1. Предсказуемость - данные не меняются неожиданно
  2. Безопасность в многопоточности - нет race conditions
  3. Легче тестировать - функции детерминированы
  4. Проще отладка - легче отследить flow данных

Terminal:

  
# Чтобы "изменить" данные, нужно пересвязать переменную
iex> list = [1, 2, 3]
iex> list = [0 | list]
iex> list
[0, 1, 2, 3]
    

Структурное разделение (Structural Sharing):

Elixir оптимизирует память, переиспользуя неизменённые части структур данных.

Terminal:

  
# При добавлении в начало списка
# новый список разделяет память с оригинальным
iex> original = [1, 2, 3]
iex> new_list = [0 | original]

# В памяти: [0] -> [1, 2, 3]
#           ↑       ↑
#       new_list  original
    

2.4 Функции для работы с типами

Проверка типов

Terminal:

  
iex> is_atom(:hello)
true

iex> is_binary("hello")
true

iex> is_boolean(true)
true

iex> is_float(3.14)
true

iex> is_integer(42)
true

iex> is_list([1, 2, 3])
true

iex> is_map(%{a: 1})
true

iex> is_tuple({1, 2, 3})
true

iex> is_number(42)
true

iex> is_number(3.14)
true
    

Преобразование типов

Terminal:

  
# String to Integer
iex> String.to_integer("123")
123

# String to Float
iex> String.to_float("3.14")
3.14

# Integer to String
iex> Integer.to_string(123)
"123"

# Float to String
iex> Float.to_string(3.14)
"3.14"

# Atom to String
iex> Atom.to_string(:hello)
"hello"

# String to Atom
iex> String.to_atom("hello")
:hello

# Tuple to List
iex> Tuple.to_list({1, 2, 3})
[1, 2, 3]

# List to Tuple
iex> List.to_tuple([1, 2, 3])
{1, 2, 3}
    

Практическое задание 2.1

Создайте файл lib/data_types_practice.ex:

Terminal:

  
defmodule DataTypesPractice do
  @moduledoc """
  Практика работы с типами данных
  """

  # 1. Функция для работы со строками
  def format_name(first_name, last_name) do
 # Объедините имя и фамилию, сделайте первые буквы заглавными
 # Подсказка: используйте String.capitalize/1
  end

  # 2. Функция для работы со списками
  def sum_list(list) do
 # Напишите функцию, которая суммирует все элементы списка
 # Подсказка: используйте рекурсию или Enum.sum/1
  end

  # 3. Функция для работы с maps
  def get_user_info(user) do
 # user - это map с ключами :name, :age, :email
 # Верните строку вида: "Name: John, Age: 30, Email: john@example.com"
  end

  # 4. Функция для работы с кортежами
  def swap_tuple({a, b}) do
 # Поменяйте местами элементы кортежа
  end

  # 5. Функция для работы с keyword lists
  def create_options(title, show_border \\ true, color \\ :blue) do
 # Верните keyword list с опциями
  end
end
    

Решение практического задания 2.1

Terminal:

  
defmodule DataTypesPractice do
  @moduledoc """
  Практика работы с типами данных
  """

  def format_name(first_name, last_name) do
 capitalized_first = String.capitalize(first_name)
 capitalized_last = String.capitalize(last_name)
 "#{capitalized_first} #{capitalized_last}"
  end

  def sum_list([]), do: 0
  def sum_list([head | tail]) do
 head + sum_list(tail)
  end

  # Или проще:
  # def sum_list(list), do: Enum.sum(list)

  def get_user_info(user) do
 "Name: #{user.name}, Age: #{user.age}, Email: #{user.email}"
  end

  def swap_tuple({a, b}) do
 {b, a}
  end

  def create_options(title, show_border \\ true, color \\ :blue) do
 [title: title, show_border: show_border, color: color]
  end
end
    

Тестирование (test/data_types_practice_test.exs)

Terminal:

  
defmodule DataTypesPracticeTest do
  use ExUnit.Case
  alias DataTypesPractice, as: DTP

  test "format_name/2 capitalizes names" do
 assert DTP.format_name("john", "doe") == "John Doe"
 assert DTP.format_name("MARY", "smith") == "Mary Smith"
  end

  test "sum_list/1 sums all elements" do
 assert DTP.sum_list([1, 2, 3, 4, 5]) == 15
 assert DTP.sum_list([]) == 0
 assert DTP.sum_list([10]) == 10
  end

  test "get_user_info/1 formats user information" do
 user = %{name: "John", age: 30, email: "john@example.com"}
 expected = "Name: John, Age: 30, Email: john@example.com"
 assert DTP.get_user_info(user) == expected
  end

  test "swap_tuple/1 swaps tuple elements" do
 assert DTP.swap_tuple({1, 2}) == {2, 1}
 assert DTP.swap_tuple({:a, :b}) == {:b, :a}
  end

  test "create_options/3 creates keyword list" do
 result = DTP.create_options("My Title")
 assert result == [title: "My Title", show_border: true, color: :blue]
    
 result = DTP.create_options("Test", false, :red)
 assert result == [title: "Test", show_border: false, color: :red]
  end
end
    

Практическое задание 2.2 (Дополнительное)

Создайте модуль для работы с корзиной покупок:

Terminal:

  
defmodule ShoppingCart do
  @moduledoc """
  Модуль для управления корзиной покупок
  """

  # Корзина представлена как map:
  # %{
  #   items: [%{name: "Apple", price: 1.50, quantity: 3}],
  #   total: 4.50
  # }

  def new do
 # Создать новую пустую корзину
  end

  def add_item(cart, name, price, quantity \\ 1) do
 # Добавить товар в корзину
  end

  def remove_item(cart, name) do
 # Удалить товар из корзины
  end

  def calculate_total(cart) do
 # Пересчитать общую стоимость
  end

  def item_count(cart) do
 # Вернуть общее количество товаров
  end
end
    

Резюме Темы 2

Ключевые понятия:

✅ Базовые типы:

  • Integer, Float
  • Boolean (только false и nil - falsy)
  • Atom - константы (:ok, :error)
  • String - UTF-8 binaries

✅ Составные типы:

  • Tuple - фиксированный размер, быстрый доступ
  • List - динамический размер, linked list
  • Keyword List - для опций функций
  • Map - key-value структура

✅ Неизменяемость:

  • Все данные immutable
  • Изменения создают новые структуры
  • Structural sharing для эффективности

✅ Функции проверки и преобразования типов

Следующий шаг: Тема 3 - Pattern matching и функции

Свежие статьи

Строки в Elixir (String)
18
ноя

Строки в Elixir (String)

таблица всех ключевых функций Elixir для работы со строками String, с кратким описанием и примером.

Прочитать
Функция List.foldl/3 в Elixir
18
ноя

Функция List.foldl/3 в Elixir

Функция List.foldl/3 выполняет операцию свёртывания (или сокращения) списка, используя предикат (функцию) и начальное значение. Давайте разберёмся, как она работает подробнее.

Прочитать
Maps - это key-value структуры данных.
18
ноя

Maps - это key-value структуры данных.

Полная таблица всех функций для работы с Map в Elixir, с короткими описаниями и примерами, в том же формате, что и предыдущие шпаргалки (для String и List).

Прочитать

Ещё статьи...

крипто-донат, на развитие сайта itdid.ru:

В новом окне с терминалом itdid.ru, введите любую сумму: