檢視原始碼 Sigils

Elixir 提供雙引號字串以及一個稱為字元清單的概念,其使用 ~c"hello world" sigil 語法定義。在本章中,我們將進一步瞭解 sigils 以及如何定義我們自己的 sigils。

Elixir 的目標之一是可擴充性:開發人員應該能夠擴充語言以符合任何特定領域。Sigils 提供了使用自訂文字表示法擴充語言的基礎。Sigils 以波浪符 (~) 字元開頭,後面接著一個小寫字母或一個或多個大寫字母,然後是一個分隔符。選用的修飾詞會加在最後一個分隔符之後。

正規表示式

Elixir 中最常見的 sigil 是 ~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 函式庫實作。Regexes 也支援修飾詞。例如,i 修飾詞會讓正規表示式不區分大小寫

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

請查看 Regex 模組,以取得有關其他修飾詞和正規表示式支援操作的更多資訊。

到目前為止,所有範例都使用 / 來分隔正規表示式。然而,sigils 支援 8 個不同的分隔符

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

支援不同分隔符的原因是提供一種編寫不含跳脫分隔符的文字常數的方法。例如,使用正斜線的正規表示式,例如 ~r(^https?://),其可讀性比 ~r/^https?:\/\// 好。類似地,如果正規表示式有正斜線和擷取群組 (使用 ()),則您可以選擇使用雙引號而不是括號。

字串、字元清單和字詞清單 sigils

除了正規表示式之外,Elixir 還附帶其他三個 sigils。

字串

符號 ~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 – Tab 鍵
  • \v – 垂直 Tab 鍵
  • \0 - 空位元組
  • \xDD - 以十六進位表示單一位元組(例如 \x13
  • \uDDDD\u{D...} - 以十六進位表示 Unicode 碼點(例如 \u{1F600}

除了這些之外,雙引號字串中的雙引號需要跳脫為 \",類似的,單引號字元清單中的單引號需要跳脫為 \'。不過,如上所見,變更分隔符號比跳脫它們更佳。

符號也支援 here-doc,也就是三個雙引號或單引號作為分隔符號

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

here-doc 符號最常見的用例是撰寫文件。例如,在文件中撰寫跳脫字元很快就會因為需要對一些字元進行雙重跳脫而容易出錯

@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_* 函式)。