Do you want to pick up from where you left of?
Take me there

字串

字串 (Strings)、字元列表 (Char Lists)、字位 (Graphemes) 和碼位 (Codepoints)。

字串 (Strings)

Elixir 的字串就是位元組序列,現在來看一個例子:

iex> string = <<104,101,108,108,111>>
"hello"
iex> string <> <<0>>
<<104, 101, 108, 108, 111, 0>>

通過將字串與位元組 0 連接,IEx 會將該字串顯示為二進位,因為它不再是有效的字串。這個技巧可以幫助我們查看任何字串的底層位元組。

註: 當使用 << >> 語法時,是對編譯器說符號內的元素為位元組。

字元列表 (Charlists)

在 Elixir 內部, 字串用位元組 (bytes) 序列而不是字元 (characters) 陣列表示。Elixir 也有一個字元列表型別(character list)。雙引號括起來的是 Elixir 字串,字元列表 (char lists) 則以單引號括起來。

這兩者有什麼不同? 字元列表中的每個值都是字元的二進位 Unicode 碼位,而這些碼位以 UTF-8 格式編碼。

現在來深入了解一下:

iex> 'hełło'
[104, 101, 322, 322, 111]
iex> "hełło" <> <<0>>
<<104, 101, 197, 130, 197, 130, 111, 0>>

322 是 ł 的 Unicode 碼位,但以 UTF-8 格式分別編碼為 197130 兩個位元組。

藉由使用 ? 可以得到字元的碼位。

iex> ?Z  
90

這允許你使用 ?Z 而不是 ‘Z’ 作為表示符號。

在 Elixir 中撰寫程式時,通常使用字串 (strings),而不是字元列表 (charlists)。不過 Elixir 也包括對字元列表的支援,因為一些 Erlang 模組需要它。

欲了解更多資訊,請參閱官方 Getting Started Guide.

字位和碼位 (Graphemes and Codepoints)

碼位只是簡單的 Unicode 字元,由一個或多個位元組表示,具體取決於 UTF-8 編碼。US ASCII 字元集外的字元始終編碼為多個位元組。例如,帶有波浪或重音的拉丁符號 (á, ñ, è) 通常編碼為兩個位元組。 亞洲語言的字元則通常被編碼為三個或四個位元組。而字位 (Graphemes) 由多個碼位組成,並以單個字元呈現。

字串模組提供了兩個函數來獲取它們:graphemes/1codepoints/1。現在來看一個例子:

iex> string = "\u0061\u0301"
"á"

iex> String.codepoints string
["a", "́"]

iex> String.graphemes string
["á"]

字串函數 (String Functions)

讓我們回顧一下字串模組中一些最重要和最有用的函數。本課程只介紹可用函數中的一個子集。要完整瀏覽整套函數集請造訪官方 String 文件。

length/1

回傳字串中的字位數目。

iex> String.length "Hello"
5

replace/3

回傳一個以新取代字串替換目前字串 pattern 的新字串。

iex> String.replace("Hello", "e", "a")
"Hallo"

duplicate/2

回傳重複原本字串 n 次的新字串。

iex> String.duplicate("Oh my ", 3)
"Oh my Oh my Oh my "

split/2

回傳由一個 pattern 分割的字串列表。

iex> String.split("Hello World", " ")
["Hello", "World"]

練習 (Exercises)

現在通過一個簡單的練習來展示,我們已經準備好使用字串!

回文構詞字串 (Anagrams)

如果有辦法重新排列 A 或 B 而使它們相等,A 和 B 將被認為是一種回文構詞字串 (anagrams)。例如:

如果重新安排字串 A 上的字元,可以得到字串 B,反之亦然。

那麼,怎麼能檢查兩個字串是不是 Elixir 中的回文構詞字串呢?最簡單的解決方法是按字母順序排序每個字串的字位,然後檢查兩個列表是否相等。現在來試試看:

defmodule Anagram do
  def anagrams?(a, b) when is_binary(a) and is_binary(b) do
    sort_string(a) == sort_string(b)
  end

  def sort_string(string) do
    string
    |> String.downcase()
    |> String.graphemes()
    |> Enum.sort()
  end
end

首先來看 anagrams?/2。我們正在查驗接收到的參數 (parameters) 是否為二進位。這是檢查參數在 Elixir 中是否為字串的方式。

之後,我們呼用一個依字母順序排序字串的函數。它首先將字串轉換為小寫字母,然後使用 String.graphemes/1 來獲取字串中的字位列表。最後,將它列入 Enum.sort/1。非常直白,對吧?

現在來檢驗一下 iex 上的輸出結果:

iex> Anagram.anagrams?("Hello", "ohell")
true

iex> Anagram.anagrams?("María", "íMara")
true

iex> Anagram.anagrams?(3, 5)
** (FunctionClauseError) no function clause matching in Anagram.anagrams?/2

    The following arguments were given to Anagram.anagrams?/2:

        # 1
        3

        # 2
        5

    iex:11: Anagram.anagrams?/2

正如所看到的,最後一次呼用 anagrams? 時觸發了一個 FunctionClauseError。這個錯誤訊息告訴我們,在目前模組中沒有函數符合所接收的兩個非二進制參數 (non-binary arguments) 的 pattern。但對這兩個所接收的字串,這正是我們不偏不倚想要的結果。

Caught a mistake or want to contribute to the lesson? Edit this lesson on GitHub!