- Elixir 教程
- Elixir - 主页
- Elixir - 概述
- Elixir - 环境
- Elixir - 基本语法
- Elixir - 数据类型
- Elixir - 变量
- Elixir - 操作员
- Elixir - 模式匹配
- Elixir - 决策
- Elixir - 弦乐
- Elixir - 角色列表
- Elixir - 列表和元组
- Elixir - 关键字列表
- Elixir - 地图
- Elixir - 模块
- Elixir - 别名
- Elixir - 功能
- Elixir - 递归
- Elixir - 循环
- Elixir - 可枚举
- Elixir - 流
- Elixir - 结构
- Elixir - 协议
- Elixir - 文件 I/O
- Elixir - 流程
- 长生不老药 - 印记
- Elixir - 领悟
- Elixir - 类型规格
- Elixir - Behave
- Elixir - 错误处理
- Elixir - 宏
- Elixir - 图书馆
- Elixir 有用资源
- Elixir - 快速指南
- Elixir - 有用的资源
- Elixir - 讨论
Elixir - 宏
宏是 Elixir 最先进、最强大的功能之一。与任何语言的所有高级功能一样,应谨慎使用宏。它们使得在编译时执行强大的代码转换成为可能。现在我们将了解什么是宏以及如何简要使用它们。
引用
在我们开始讨论宏之前,让我们首先看看 Elixir 的内部结构。Elixir 程序可以用它自己的数据结构来表示。Elixir 程序的构建块是一个包含三个元素的元组。例如,函数调用 sum(1, 2, 3) 在内部表示为 -
{:sum, [], [1, 2, 3]}
第一个元素是函数名称,第二个元素是包含元数据的关键字列表,第三个元素是参数列表。如果您编写以下内容,则可以将其作为 iex shell 中的输出获取 -
quote do: sum(1, 2, 3)
运算符也表示为这样的元组。变量也使用这样的三元组来表示,除了最后一个元素是Atomics而不是列表。当引用更复杂的表达式时,我们可以看到代码是用这样的元组表示的,这些元组通常以类似于树的结构相互嵌套。许多语言将这种表示称为抽象语法树(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 和动态添加代码到我们的应用程序所需的一切
让我们将 except 作为宏来实现。我们将首先使用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
这里发生的情况是我们的代码被except宏返回的引用代码所替换。我们取消了表达式的引号以在当前上下文中对其求值,并且取消了 do 块的引号以在其上下文中执行它。这个例子向我们展示了在 Elixir 中使用宏进行元编程。
宏可用于更复杂的任务,但应谨慎使用。这是因为元编程通常被认为是一种不好的做法,应该仅在必要时使用。