📜  Elixir-宏

📅  最后修改于: 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剂中的宏进行元编程。

宏可以用于更复杂的任务中,但应谨慎使用。这是因为一般而言,元编程被认为是不好的做法,仅应在必要时使用。