查看源代码 标识符

Elixir 提供双引号字符串,以及称为字符列表的概念,字符列表使用 ~c"hello world" 标识符语法定义。在本节中,我们将进一步了解标识符以及如何定义我们自己的标识符。

Elixir 的目标之一是可扩展性:开发人员应该能够扩展语言以适应任何特定的领域。标识符为使用自定义文本表示形式扩展语言提供了基础。标识符以波浪号 (~) 字符开头,后面紧跟着一个或多个大写字母,然后是一个分隔符。可选修饰符在最终分隔符之后添加。

正则表达式

Elixir 中最常见的标识符是 ~r,它用于创建 正则表达式

# A regular expression that matches strings which contain "foo" or "bar":
iex> regex = ~r/foo|bar/
~r/foo|bar/
iex> "foo" =~ regex
true
iex> "bat" =~ regex
false

Elixir 提供 Perl 兼容的正则表达式 (regexes),如 PCRE 库实现。正则表达式也支持修饰符。例如,i 修饰符使正则表达式不区分大小写

iex> "HELLO" =~ ~r/hello/
false
iex> "HELLO" =~ ~r/hello/i
true

查看 Regex 模块,以了解更多关于其他修饰符以及支持的正则表达式操作信息。

到目前为止,所有示例都使用 / 来分隔正则表达式。但是,标识符支持 8 种不同的分隔符

~r/hello/
~r|hello|
~r"hello"
~r'hello'
~r(hello)
~r[hello]
~r{hello}
~r<hello>

支持不同分隔符的原因是提供一种在不转义分隔符的情况下编写字面量的方法。例如,包含正斜杠的正则表达式,例如 ~r(^https?://),读起来比 ~r/^https?:\/\// 更好。类似地,如果正则表达式包含正斜杠和捕获组(使用 ()),那么可以选择双引号而不是圆括号。

字符串、字符列表和单词列表标识符

除了正则表达式之外,Elixir 还附带了另外三个标识符。

字符串

~s 标识符用于生成字符串,就像双引号一样。当字符串包含双引号时,~s 标识符很有用

iex> ~s(this is a string with "double" quotes, not 'single' ones)
"this is a string with \"double\" quotes, not 'single' ones"

字符列表

~c 标识符是表示字符列表的常规方式。

iex> [?c, ?a, ?t]
~c"cat"
iex> ~c(this is a char list containing "double quotes")
~c"this is a char list containing \"double quotes\""

单词列表

~w 标识符用于生成单词列表(*单词* 只是普通的字符串)。在 ~w 标识符内,单词由空格分隔。

iex> ~w(foo bar bat)
["foo", "bar", "bat"]

~w 标识符还接受 csa 修饰符(分别用于字符列表、字符串和原子),它们指定结果列表中元素的数据类型

iex> ~w(foo bar bat)a
[:foo, :bar, :bat]

字符串标识符中的插值和转义

Elixir 支持一些标识符变体来处理转义字符和插值。特别是,大写字母标识符不执行插值也不执行转义。例如,虽然 ~s~S 都将返回字符串,但前者允许转义代码和插值,而后者则不允许

iex> ~s(String with escape codes \x26 #{"inter" <> "polation"})
"String with escape codes & interpolation"
iex> ~S(String without escape codes \x26 without #{interpolation})
"String without escape codes \\x26 without \#{interpolation}"

以下转义代码可用于字符串和字符列表

  • \\ – 单个反斜杠
  • \a – 响铃/警报
  • \b – 退格键
  • \d - 删除
  • \e - 转义
  • \f - 换页
  • \n – 换行
  • \r – 回车
  • \s – 空格
  • \t – 制表符
  • \v – 垂直制表符
  • \0 - 空字节
  • \xDD - 用十六进制表示单个字节(例如 \x13
  • \uDDDD\u{D...} - 用十六进制表示 Unicode 代码点(例如 \u{1F600}

除了这些之外,双引号字符串中的双引号需要转义为 \",并且类似地,单引号字符列表中的单引号需要转义为 \'。但是,最好像上面那样更改分隔符,而不是转义它们。

标识符也支持 heredocs,即三个双引号或单引号作为分隔符

iex> ~s"""
...> this is
...> a heredoc string
...> """

heredoc 标识符最常见的用例是编写文档。例如,在文档中编写转义字符很快就会变得容易出错,因为需要对某些字符进行双重转义

@doc """
Converts double-quotes to single-quotes.

## Examples

    iex> convert("\\\"foo\\\"")
    "'foo'"

"""
def convert(...)

通过使用 ~S,可以完全避免此问题

@doc ~S"""
Converts double-quotes to single-quotes.

## Examples

    iex> convert("\"foo\"")
    "'foo'"

"""
def convert(...)

日历标识符

Elixir 提供了多个标识符来处理各种时间和日期格式。

日期

一个 %Date{} 结构包含字段 yearmonthdaycalendar。可以使用 ~D 标识符创建一个

iex> d = ~D[2019-10-31]
~D[2019-10-31]
iex> d.day
31

时间

%Time{} 结构包含字段 hourminutesecondmicrosecondcalendar。可以使用 ~T 标识符创建一个

iex> t = ~T[23:00:07.0]
~T[23:00:07.0]
iex> t.second
7

NaiveDateTime

%NaiveDateTime{} 结构包含 DateTime 中的字段。可以使用 ~N 标识符创建一个

iex> ndt = ~N[2019-10-31 23:00:07]
~N[2019-10-31 23:00:07]

为什么称为 naive?因为它不包含时区信息。因此,给定的日期时间可能根本不存在,或者它可能在某些时区中存在两次 - 例如,当我们为了夏令时而将时钟拨快或拨慢时。

UTC 日期时间

一个 %DateTime{} 结构包含与 NaiveDateTime 相同的字段,此外还增加了跟踪时区的字段。 ~U 标识符允许开发人员在 UTC 时区中创建 DateTime

iex> dt = ~U[2019-10-31 19:59:03Z]
~U[2019-10-31 19:59:03Z]
iex> %DateTime{minute: minute, time_zone: time_zone} = dt
~U[2019-10-31 19:59:03Z]
iex> minute
59
iex> time_zone
"Etc/UTC"

自定义标识符

正如本章开头所暗示的那样,Elixir 中的标识符是可扩展的。实际上,使用标识符 ~r/foo/i 等同于调用带有二进制文件和字符列表作为参数的 sigil_r

iex> sigil_r(<<"foo">>, [?i])
~r"foo"i

我们可以通过 sigil_r 访问 ~r 标识符的文档

iex> h sigil_r
...

我们还可以通过实现遵循 sigil_{character} 模式的函数来提供我们自己的标识符。例如,让我们实现返回整数的 ~i 标识符(可选 n 修饰符使其为负数)

iex> defmodule MySigils do
...>   def sigil_i(string, []), do: String.to_integer(string)
...>   def sigil_i(string, [?n]), do: -String.to_integer(string)
...> end
iex> import MySigils
iex> ~i(13)
13
iex> ~i(42)n
-42

自定义标识符可以是单个小写字母,也可以是多个大写字母。

标识符还可以使用宏来执行编译时工作。例如,Elixir 中的正则表达式在源代码编译期间被编译成高效的表示形式,因此在运行时跳过了此步骤。如果您对该主题感兴趣,您可以了解更多关于宏的信息,并查看标识符如何在 Kernel 模块(其中定义了 sigil_* 函数)中实现的。