Elixir入门教程-印记

  1. 正则表达式
  2. 字符串、字符列表和词列表印记
    2.1 字符串
    2.2 字符列表
    2.3 词列表
  3. 在印记中插入文本和转义字符
  4. 自定义印记

我们已经学了Elixir提供双引号字符串和单引号字符列表。然而,这只是涵盖了该语言有文本表示的数据类型结构的表面。例如,原子主要就是通过 :atom 表示来创建的。

Elixir其中一个目标是可扩展性:开发者可以扩展该语言来解决任何特定的领域问题。计算机科学已经成为如此广阔的领域,一门语言解决许多领域的问题来作为它的核心部分是不太可能的。相反,我们最好的方法是使语言具有可扩展性,因此开发人员、公司和社区可以将语言扩展到相关领域。

本章,我们将探索印记,它是由Elixir这门语言提供的处理文本描述数据类型的其中一种机制。印记由波浪字符(~)开始,紧接着是一个字符(这个字符定义本印记),然后是一个分隔符,可选性的修饰符可以增加在最后的分隔符后。

正则表达式

Elixir里最常用的印记是~r,它被用来创建正则表达式

1
2
3
4
5
6
7
# 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库实现。regexes也支持修饰符。例如,i修饰符使得正则表达式不大小写敏感:

1
2
3
4
iex> "HELLO" =~ ~r/hello/
false
iex> "HELLO" =~ ~r/hello/i
true

请查阅Regex模块获取正则表达式的其他修饰符和被支持的操作的信息。

到目前为止,所有例子都是用 / 来界定一个正则表达式。然后印记支持8种不同的分隔符:

1
2
3
4
5
6
7
8
~r/hello/
~r|hello|
~r"hello"
~r'hello'
~r(hello)
~r[hello]
~r{hello}
~r<hello>

支持不同分隔符背后的原因是提供一种不需要转义分隔符来写字面量。例如,一个有斜杠的正则表达式,这样写:~r(^https?://) 肯定比这么写 ~r/^https?:\/\/ 更容易读。相似地,如果正则表达式含有斜杠和捕获组(即使用()),那么你可以使用双引号来替代圆括号。

字符串、字符列表和词列表印记

除了正则表达式,Elixir还有三种其他印记。

字符串

~s印记被用来创建字符串,像双引号一样。当一个字符串包含有双引号的时候,~s印记很有用:

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

字符列表

~c印记被用来创建含有单引号的字符列表:

1
2
iex> ~c(this is a char list containing 'single quotes')
'this is a char list containing \'single quotes\''

词列表

~w印记被用来创建词列表(词就是普通字符串)。在~w印记里,词被空白分隔。

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

~w印记也接收c, s 和 a 修饰符(分别对应,字符列表、字符串和原子),它们指定结果列表的元素的数据类型:

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

在印记中插入文本和转义字符

除了小写印记,Elixir也支持大写印记来处理转义字符和插入文本。虽然 ~s 和 ~S 都返回字符串,但是前者允许转义码和插入文本,而后者则不允许:

1
2
3
4
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 - Tab键
  • \v - 垂直Tab键
  • \0 - 空字节
  • \xDD - 用十六进制表示的单字节(比如:\x13)
  • \uDDDD 和 \u{D…} - 用十六进制表示的Unicode代码点(比如:\u{1F600})

另外,在双引号字符串里的双引号需要用\”来转义,类似的,单引号字符列表里的单引号需要用\’来转义。然而,向上述的例子那样改变分隔符而不是转义它们是一种更好的方式。

印记也支持 heredocs ,即 三个双或单引号作为分隔符:

1
2
3
4
iex> ~s"""
...> this is
...> a heredoc string
...> """

heredocs印记最常用的地方是在写文档的时候。例如,在文档里写转义字符很容易出错,因为需要两次转义一些字符:

1
2
3
4
5
6
7
8
9
10
@doc """
Converts double-quotes to single-quotes.
## Examples
iex> convert("\\\"foo\\\"")
"'foo'"
"""
def convert(...)

但是用~S,这个问题可以完全避免:

1
2
3
4
5
6
7
8
9
10
@doc ~S"""
Converts double-quotes to single-quotes.
## Examples
iex> convert("\"foo\"")
"'foo'"
"""
def convert(...)

自定义印记

如本文开始的时候所提示,Elixir中的印记是一种扩展。实际上,用印记 ~r/foo/i 和以一个二进制数据和一个字符列表作为入参调用 sigil_r 是等效的:

1
2
iex> sigil_r(<<"foo">>, 'i')
~r"foo"i

我们可以通过访问sigil_r文档来看看~r:

1
2
iex> h sigil_r
...

我们也可以通过遵循 sigil_{identifier} 模式来实现函数以提供我们自己的印记。例如,让我们来实现 ~i 印记,它返回一个整数(使用可选择的 n 修饰符来返回它的负数):

1
2
3
4
5
6
7
8
9
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_*相关被定义的函数)里是如何实现的。

原文链接: http://elixir-lang.org/getting-started/sigils.html