Elixir: Новые возможности
Макросы позволяют добавлять в язык новые команды, как в предыдущем упражнении:
defmodule Solution do
defmacro my_unless(condition, do: expression) do
quote do
if(!unquote(condition), do: unquote(expression))
end
end
endНа самом деле, в Elixir все конструкции имеют внутреннее представление, даже функции. Это означает, что с помощью макроса можно сгенерировать функцию:
defmodule Exercise do
defmacro create_multiplier(fn_name, factor) do
quote do
def unquote(fn_name)(value) do
unquote(factor) * value
end
end
end
endЧтобы воспользоваться этим макросом, нужно создать другой модуль, так как определение функции доступно только внутри модуля:
defmodule MyModule do
require Exercise
Exercise.create_multiplier(:double, 2)
Exercise.create_multiplier(:triple, 3)
def run_example() do
x = double(2)
IO.puts("Two times 2 is #{x}")
end
end
MyModule.double(5)
# => 10
MyModule.triple(3)
# => 9
MyModule.run_example()
# => Two times 2 is 4Создадим универсальный макрос, который за один вызов создает нужное количество функций:
defmodule Exercise do
defmacro create_functions(fn_list) do
Enum.map(fn_list, fn {name, factor} ->
quote do
def unquote(:"#{name}_value")(value) do
unquote(factor) * value
end
end
end)
end
endИ теперь опробуем макрос в новом модуле:
defmodule Example do
require Exercise
Exercise.create_functions([{:double, 2}, {:triple, 3}, {:nullify, 0}])
end
Example.double_value(2)
# => 4
Example.triple_value(2)
# => 6
Example.nullify_value(2)
# => 0Если нужно создать макрос только внутри модуля, то defmacrop то что нужно. Как и приватные функции, макрос объявленный таким образом, будет доступен только в модуле, где макрос объявлен и только во время компиляции.
Задание
Создайте макрос prohibit_words, генерирующий функцию forbidden?, в который передается список запрещенных слов и проверяется, запрещено ли слово, переданное в функцию forbidden?. Если передано не слово, то функция возвращает false:
defmodule Exercise
require Solution
Solution.prohibit_words(["hello", "world", "foo"])
end
Exercise.forbidden?("hello")
# => true
Exercise.forbidden?("test")
# => false
Exercise.forbidden?(1)
# => false
Exercise.forbidden?(%{hello: :world})
# => falseElixir: Новые возможности
Макросы позволяют добавлять в язык новые команды, как в предыдущем упражнении:
defmodule Solution do
defmacro my_unless(condition, do: expression) do
quote do
if(!unquote(condition), do: unquote(expression))
end
end
endНа самом деле, в Elixir все конструкции имеют внутреннее представление, даже функции. Это означает, что с помощью макроса можно сгенерировать функцию:
defmodule Exercise do
defmacro create_multiplier(fn_name, factor) do
quote do
def unquote(fn_name)(value) do
unquote(factor) * value
end
end
end
endЧтобы воспользоваться этим макросом, нужно создать другой модуль, так как определение функции доступно только внутри модуля:
defmodule MyModule do
require Exercise
Exercise.create_multiplier(:double, 2)
Exercise.create_multiplier(:triple, 3)
def run_example() do
x = double(2)
IO.puts("Two times 2 is #{x}")
end
end
MyModule.double(5)
# => 10
MyModule.triple(3)
# => 9
MyModule.run_example()
# => Two times 2 is 4Создадим универсальный макрос, который за один вызов создает нужное количество функций:
defmodule Exercise do
defmacro create_functions(fn_list) do
Enum.map(fn_list, fn {name, factor} ->
quote do
def unquote(:"#{name}_value")(value) do
unquote(factor) * value
end
end
end)
end
endИ теперь опробуем макрос в новом модуле:
defmodule Example do
require Exercise
Exercise.create_functions([{:double, 2}, {:triple, 3}, {:nullify, 0}])
end
Example.double_value(2)
# => 4
Example.triple_value(2)
# => 6
Example.nullify_value(2)
# => 0Если нужно создать макрос только внутри модуля, то defmacrop то что нужно. Как и приватные функции, макрос объявленный таким образом, будет доступен только в модуле, где макрос объявлен и только во время компиляции.
Задание
Создайте макрос prohibit_words, генерирующий функцию forbidden?, в который передается список запрещенных слов и проверяется, запрещено ли слово, переданное в функцию forbidden?. Если передано не слово, то функция возвращает false:
defmodule Exercise
require Solution
Solution.prohibit_words(["hello", "world", "foo"])
end
Exercise.forbidden?("hello")
# => true
Exercise.forbidden?("test")
# => false
Exercise.forbidden?(1)
# => false
Exercise.forbidden?(%{hello: :world})
# => falseВаше упражнение проверяется по этим тестам
defmodule Test do
use ExUnit.Case
defmodule Exercise do
require Solution
Solution.prohibit_words(["hello", "world", "foo"])
end
test "prohibit_words work" do
assert Exercise.forbidden?("hello")
assert Exercise.forbidden?("world")
assert Exercise.forbidden?("foo")
refute Exercise.forbidden?("baz")
refute Exercise.forbidden?(2)
refute Exercise.forbidden?(%{hello: :world})
end
endРешение учителя откроется через:
20:00
