Logo
Книга для начинающих
ВходРегистрация
/
Программирование
/
Курс Elixir
/

Знакомство с макросами

Elixir: Знакомство с макросами

Иногда возникает необходимость расширить сам язык, создав предметно-ориентированный язык (DSL), либо реализовать какие-то новые возможности. Elixir с помощью макросов позволяет достичь этого, а сам процесс написания макросов называется метапрограммированием.

Макросы похожи на функции, но действуют совершенно иначе, чем функции. Объявляется макрос с помощью инструкции defmacro. Для начала рассмотрим отличия макросов и функций:

# объявим модуль с функцией и макросом
defmodule Exercise do
  def my_fn(x) do
    IO.inspect(x)
    x
  end

  defmacro my_macro(x) do
    IO.inspect(x)
    x
  end
end

Чтобы воспользоваться макросом, нужно подключить модуль с помощью инструкции require в котором макрос объявлен:

require Exercise

Exercise.my_fn(1 + 2)
# => 3
# => 3

Exercise.my_macro(1 + 2)
# => {:+, [line: 12], [1, 2]}
# => 3

Вызов функции понятен, Elixir вычисляет значение выражения 1 + 2, затем передает его дальше в функцию, где полученное значение выводится на экран и возвращается из функции. В случае с макросом, вместо вычисления выражения, оно интерпретируется как кортеж, затем этот кортеж выводится на экран и возвращается из макроса, а после интерпретатор Elixir вычисляет возвращенный кортеж.

Рассмотрим внимательнее кортеж, который оказался в макросе {:+, [line: 12], [1, 2]}, где :+ - оператор, [line: 12] - метаданные об операции, [1, 2] - список операндов. Теперь, обладая пониманием, что означает этот кортеж, напишем макрос, который удваивает переданный аргумент:

defmodule Exercise do
  defmacro double(x) do
    {:*, [], [2, x]}
  end
end

require Exercise

Exercise.double(2)
# => 4
Exercise.double(10)
# => 20
Exercise.double(2 * 2)
# => 8
Exercise.double(2 + 1 - 5)
# => -4

Макрос работает, но выглядит неудобно, в следующем упражнении рассмотрим как сделать макрос более читаемым.

Интересный факт: создатель языка, Жозе Валим, при добавлении макросов в Elixir, вдохновлялся Clojure.

Задание

Создайте макрос my_abs, который берет абсолютное значение переданного аргумента (поможет функция abs):

require Solution

Solution.my_abs(-2)
# => 2
Solution.my_abs(2)
# => 2
Solution.my_abs(-5 * 100)
# => 500
Solution.my_abs(-2 - 100 + 1)
# => 101

Полезное

  • Официальная документация

  • Про DSL

  • Про метапрограммирование

Команда проекта находится в телеграм-сообществе. Там можно задать любой вопрос и повлиять на проект

Если вы зашли в тупик, то самое время поговорить с нашим асситентом Тота во вкладке "ИИ-помощник":

Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.

Это нормально 🙆, в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи. В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.

Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в обратной связи нашего сообщества

Нашли ошибку? Есть что добавить? Пулреквесты приветствуются
/
Программирование
/
Курс Elixir
/

Знакомство с макросами

Elixir: Знакомство с макросами

Иногда возникает необходимость расширить сам язык, создав предметно-ориентированный язык (DSL), либо реализовать какие-то новые возможности. Elixir с помощью макросов позволяет достичь этого, а сам процесс написания макросов называется метапрограммированием.

Макросы похожи на функции, но действуют совершенно иначе, чем функции. Объявляется макрос с помощью инструкции defmacro. Для начала рассмотрим отличия макросов и функций:

# объявим модуль с функцией и макросом
defmodule Exercise do
  def my_fn(x) do
    IO.inspect(x)
    x
  end

  defmacro my_macro(x) do
    IO.inspect(x)
    x
  end
end

Чтобы воспользоваться макросом, нужно подключить модуль с помощью инструкции require в котором макрос объявлен:

require Exercise

Exercise.my_fn(1 + 2)
# => 3
# => 3

Exercise.my_macro(1 + 2)
# => {:+, [line: 12], [1, 2]}
# => 3

Вызов функции понятен, Elixir вычисляет значение выражения 1 + 2, затем передает его дальше в функцию, где полученное значение выводится на экран и возвращается из функции. В случае с макросом, вместо вычисления выражения, оно интерпретируется как кортеж, затем этот кортеж выводится на экран и возвращается из макроса, а после интерпретатор Elixir вычисляет возвращенный кортеж.

Рассмотрим внимательнее кортеж, который оказался в макросе {:+, [line: 12], [1, 2]}, где :+ - оператор, [line: 12] - метаданные об операции, [1, 2] - список операндов. Теперь, обладая пониманием, что означает этот кортеж, напишем макрос, который удваивает переданный аргумент:

defmodule Exercise do
  defmacro double(x) do
    {:*, [], [2, x]}
  end
end

require Exercise

Exercise.double(2)
# => 4
Exercise.double(10)
# => 20
Exercise.double(2 * 2)
# => 8
Exercise.double(2 + 1 - 5)
# => -4

Макрос работает, но выглядит неудобно, в следующем упражнении рассмотрим как сделать макрос более читаемым.

Интересный факт: создатель языка, Жозе Валим, при добавлении макросов в Elixir, вдохновлялся Clojure.

Задание

Создайте макрос my_abs, который берет абсолютное значение переданного аргумента (поможет функция abs):

require Solution

Solution.my_abs(-2)
# => 2
Solution.my_abs(2)
# => 2
Solution.my_abs(-5 * 100)
# => 500
Solution.my_abs(-2 - 100 + 1)
# => 101

Полезное

  • Официальная документация

  • Про DSL

  • Про метапрограммирование

Команда проекта находится в телеграм-сообществе. Там можно задать любой вопрос и повлиять на проект

Если вы зашли в тупик, то самое время поговорить с нашим асситентом Тота во вкладке "ИИ-помощник":

Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.

Это нормально 🙆, в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи. В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.

Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в обратной связи нашего сообщества

Нашли ошибку? Есть что добавить? Пулреквесты приветствуются
← ПредыдущийСледующий →
← ПредыдущийСледующий →
← ПредыдущийСледующий →

Ваше упражнение проверяется по этим тестам

defmodule Test do
  use ExUnit.Case

  require Solution

  test "my_abs macro work" do
    assert Solution.my_abs(-2) == 2
    assert Solution.my_abs(2) == 2
    assert Solution.my_abs(-5 * 100) == 500
    assert Solution.my_abs(-2 - 100 + 1) == 101
  end
end
← ПредыдущийСледующий →

Решение учителя откроется через:

20:00

waiting_clock
← ПредыдущийСледующий →