📅  最后修改于: 2020-11-04 08:36:58             🧑  作者: Mango
宏是Elixir的最高级和最强大的功能之一。与任何语言的所有高级功能一样,应谨慎使用宏。它们使在编译时执行强大的代码转换成为可能。现在,我们将简要了解什么是宏以及如何使用它们。
在我们开始讨论宏之前,让我们首先看一下Elixir内部。 Elixir程序可由其自己的数据结构表示。 Elixir程序的基础是具有三个元素的元组。例如,函数调用sum(1、2、3)在内部表示为-
{:sum, [], [1, 2, 3]}
第一个元素是函数名称,第二个元素是包含元数据的关键字列表,第三个元素是参数列表。如果编写以下代码,则可以将其作为iex shell中的输出获取-
quote do: sum(1, 2, 3)
运算符也表示为此类元组。变量也使用这种三元组来表示,除了最后一个元素是原子而不是列表。当引用更复杂的表达式时,我们可以看到代码以这样的元组表示,它们通常彼此嵌套在一个类似于树的结构中。许多语言会将此类表示称为抽象语法树(AST) 。 Elixir调用这些引用的表达式。
现在我们可以检索代码的内部结构了,如何修改它?要注入新的代码或值,我们使用unquote 。当我们取消对表达式的引用时,它将被评估并注入AST中。让我们考虑一个示例(在iex shell中)以了解概念-
num = 25
quote do: sum(15, num)
quote do: sum(15, unquote(num))
运行上述程序时,将产生以下结果-
{:sum, [], [15, {:num, [], Elixir}]}
{:sum, [], [15, 25]}
在引用表达式的示例中,它没有自动将num替换为25。如果要修改AST,则需要取消引用此变量。
因此,既然我们熟悉引用和取消引用,我们就可以使用宏在Elixir中探索元编程。
用最简单的术语来说,宏是特殊功能,旨在返回一个带引号的表达式,该表达式将插入到我们的应用程序代码中。想象一下,宏将被带引号的表达式代替,而不是像函数那样函数。使用宏,我们拥有扩展Elixir并向应用程序动态添加代码所需的一切
让我们实现,除非作为宏。我们将从使用defmacro宏定义宏开始。请记住,我们的宏需要返回带引号的表达式。
defmodule OurMacro do
defmacro unless(expr, do: block) do
quote do
if !unquote(expr), do: unquote(block)
end
end
end
require OurMacro
OurMacro.unless true, do: IO.puts "True Expression"
OurMacro.unless false, do: IO.puts "False expression"
运行上述程序时,将产生以下结果-
False expression
此处发生的是我们的代码已被除非宏返回的引用代码替换。我们已取消引用表达式以在当前上下文中对其求值,也未引用do块以在其上下文中执行它。此示例向我们展示了使用e剂中的宏进行元编程。
宏可以用于更复杂的任务中,但应谨慎使用。这是因为一般而言,元编程被认为是不好的做法,仅应在必要时使用。