Logo
Section 1 / 9

实现一个 if 语句

学习 Elixir 中 'if' 语句的实现。

导入

实现一个 if 语句

学习 Elixir 中 'if' 语句的实现。

引言

你是否曾感到沮丧,因为某种语言缺少某个恰好适合你正在编写的代码的功能?或者你是否发现自己在重复一些难以拆分为函数的代码片段?又或者,你只是希望能够更贴近问题领域来编写程序?

如果是这样,那么你一定会喜欢本章内容。

但在深入细节之前,请注意一个警告:宏很容易让代码变得难以理解,因为我们实际上是在重写语言的部分功能。因此,只要能用函数实现,我们就绝不使用宏。让我们再次强调:

警告:只要能用函数,就绝不用宏。

事实上,我们通常不会在普通应用代码中编写宏。但如果你正在编写一个库,并希望使用某些元编程技术,那么了解宏的工作原理就至关重要。

if 语句

假设 Elixir 没有 if 语句,只有 case。虽然我们可以接受放弃 while 循环,但没有 if 语句实在难以忍受。于是,我们决定自己实现一个。

我们希望像这样调用它:

1myif «条件» do 2 «条件为真时执行» 3else 4 «条件为假时执行» 5end

我们知道,在 Elixir 中,代码块会被转换为关键字参数,因此这等价于:

1myif «条件», 2 do: «条件为真时执行», 3 else: «条件为假时执行»

一个示例调用如下:

1My.myif 1==2, do: (IO.puts "1 == 2"), else: (IO.puts "1 != 2")

我们尝试将 myif 实现为一个函数:

1defmodule My do 2 def myif(condition, clauses) do 3 do_clause = Keyword.get(clauses, :do, nil) 4 else_clause = Keyword.get(clauses, :else, nil) 5 case condition do 6 val when val in [false, nil] 7 -> else_clause 8 _otherwise 9 -> do_clause 10 end 11 end 12end

我们可以用以下命令来测试运行:

1iex> c "My.exs" 2[My] 3iex> My.myif 1==2, do: (IO.puts "1 == 2"), else: (IO.puts "1 != 2") 41 == 2 51 != 2 6:ok

输出结果可能令人意外。

当我们调用 myif 函数时,Elixir 必须在传入参数前先计算所有参数的值。因此,do: 和 else: 两个分支都会被立即执行,所以我们看到了它们的输出。由于 IO.puts 成功时返回 :ok,实际传递给 myif 的是:

1myif 1==2, do: :ok, else: :ok

这正是最终返回 :ok 的原因。

我们需要一种方法来延迟执行这些代码块。这时,宏就派上用场了。但在实现我们的 myif 宏之前,需要先了解一些背景知识。

任务与代理

宏注入代码