這是 w3HexSchool 鼠年全馬鐵人挑戰 Week 4 唷
這是 Elixir 從零開始 系列 03 唷
前言
前一篇講完 function 和 module,現在我們來回頭介紹一些基本的型別系統囉
基礎型別
Numbers
整數
1 | iex(1)> 3 |
二進位、八進位和十六進位
1 | iex(4)> 0xff |
浮點數
1 | iex(3)> 3.14 |
一個神奇的語法糖,底線會被 Elixir 忽略,通常用來方便閱讀標記
1 | iex(10)> 1_000_000 |
Atoms
Atom
Atom 是一個被命名的常數,它很類似 C++ 的 enumeration,在使用上以冒號開頭,後面搭配一個名稱,而這個名稱就代表它本身(的值),在 runtime 的時候,這個名稱就會被存在 atom table 內,當你把一個 atom 指派給某個變數的時候,這個 atom 不會直接被指派而是使用參考的方式從 atom table 中使用 atom
我對於 atom 的理解就是,把它當作一個 key-value 的 key 使用,這個 key 本身沒有意義,就只是一個名稱,只有在 key 有對應關係時才有意義,舉個例子,我想要創造一個變數叫做 name
並且把這個變數當作是 key-value 中的 key,我只要建立一個 atom 名為 :name
就可以使用它了,如下範例,這個 atom 在 map 中的關係就對應到 "Bob"
這個字串這樣子
1 | :name |
以下這裡我們建立兩個 atom,可以看到這兩個(的值)是不相等的
1 | iex(9)> :foo |
Atoms as Booleans
在 Elixir 中,並沒有專用的 Boolean 型別,反而使用 :true
和 :false
這兩個 atom,這裡也有一個語法糖,我們可以拿掉冒號,如下例子
1 | iex(1)> :true == true |
Falsy and truthy values
在 C++ 中 的 null 在 Elixir 中是一個叫 :nil
的 atom,它跟 Booleans 一樣有語法糖可以省略冒號
1 | iex(1)> nil == :nil |
在 Elixir 中有一個特別的規則 falsy and truthy values,它的分類如下表格
truthy | falsy |
---|---|
everything else | nil and false |
由於有這樣的一個規則存在,我們可以看到以下
1 | iex(1)> nil || false || 5 || true |
nil
和 false
是 falsy,而 5
是 truthy,因為 ||
的特性為有 true 之後的後面值就可以忽略,所以上面這行最後結果回傳是 5
strings
根據 Elixir in Action 的介紹,字串解釋如下
Elixir uses UTF-8 to encode its strings, which means that code points are encoded as a series of bytes.
因為字串是 bytes 的組合,所我們要注意一個特性,如下
1 | iex(1)> is_binary("hello") |
直接來看一些範例
1 | iex(1)> "This is a string" |
字串插值的用法為 #{}
1 | iex(2)> "Embedded expression: #{3 + 0.14}" |
字串串接,我們使用 <>
運算子來串接
1 | iex(1)> name = "Sean" |
可以支援多行字串,我們稱呼它 heredocs syntax
1 | iex(9)> """ |
新介紹!
字元列表(character list)
1
2 iex(10)> 'hełło'
[104, 101, 322, 322, 111]使用雙引號表示為字串,使用單引號則表示為字元列表,在 Elixir 中我們通常是使用字串,會用到字元列表是因為有些純 Erlang library 用到,這裡就不多解釋
sigils
這裡還有一個語法要介紹 sigils,這個中文翻譯叫符咒?!所有的符咒將以波浪符號 ~
開頭,已經有內建一些了,或是我們也可以自創符咒,這部分有興趣可以自行 Google,這邊我只介紹跟字串有關的符咒
~S
~s
小寫 s 可以處裡字串插值,而大寫就是原封不動的印出來所有的東西
1 | iex(7)> ~S|fff #{3+0.14}| |
用來分隔符號清單如下,這些都可以用
<...>
尖括號 (pointy brackets){...}
大括號 (curly brackets)[...]
中括號 (square brackets)(...)
小括號 (parentheses)|...|
管線符號 (pipes)/.../
斜線 (forward slashes)"..."
雙引號 (double quotes)'...'
單引號 (single quotes)
1 | iex(3)> ~s/the cat in the hat on the mat/ |
(Linked) Lists
列表(List)是資料的群集,使用中括號 [...]
定義,裡面可以包含任何型態唷
1 | iex(1)> [3.14, :pie, "Apple"] |
列表看起來像是 array 但是它其實比較像是 singly linked list,所以多數針對列表的操作都是 O(n),比如說函式 Kernel.length/1 或是 Enum.at/2,這兩個函式的操作都要從頭開始執行,時間複雜度就是 O(n),因此,有一個特性要特別提醒的就是,通常前置插入 (prepend) 比後綴置入 (append) 更快
以下是 O(n) 操作
1 | iex(2)> length(prime_numbers) |
以下是 prepend vs append
1 | iex(4)> list = [3.14, :pie, "Apple"] |
列表串接使用 ++/2
運算子
1 | iex(11)> [1, 2, 3] ++ [4, 5] |
通過提供 --/2
運算子支援減法;即使減去不存在的值也是安全的
1 | iex(12)> ["foo", :bar, 42] -- [42, "bar"] |
注意重複的值。對於右邊的每個元素,左邊中第一個出現的將被移除
1 | iex(13)> [1,2,2,3,2,3] -- [1,2,3,2] |
注意!
減法為嚴格匹配
1
2
3
4 iex(1)> [2] -- [2.0]
[2]
iex(2)> [2.0] -- [2.0]
[]
使用列表時,常常操作列表的頭和尾。 頭是列表的第一個元素,而尾是包含剩餘元素的列表。 在這個部份的操作中 Elixir 提供了兩個有用的函式 hd
和 tl
1 | iex(14)> hd [3.14, :pie, "Apple"] |
Tuples
中文翻譯成元組?!我覺得這翻譯很奇怪就是了。這是一組沒有型態的結構,使用大括號 {...}
來定義
1 | iex(1)> person = {"Bob", 25} |
我們可以很常看到用來做為函式回傳接收
1 | iex(1)> File.read("path/to/existing/file") |
Maps
映射是一組 key-value 的對應關係,我們使用 %{}
來定義它,並使用 =>
來做對應
1 | iex(2)> squares = %{1 => 1, 2 => 4, 3 => 9} |
我們也可這樣用
1 | iex(8)> map.foo |
2020/03/17 update:
有另外一種特殊的語法,使用冒號來區分,如下範例
1 | iex> %{foo: "bar", hello: "world"} |
Keyword lists
關鍵字列表其實是上面介紹過的 list 和 tuple 的組合,我們直接看範例
1 | [ method: "", path: "", resp_body: "", status: nil ] |
事實上在內部表示為
1 | [ {:method, ""}, {:path, ""}, {:resp_body, ""}, {:status, nil} ] |
所以呢,我們可以總結幾點
- 關鍵字列表就是一個列表(list)
- 列表內的元素全部都是 tuple
- Tuple 的第一個元素必須為 atom
基礎運算
基本的四則運算不可少!
1 | iex(1)> 2 + 2 |
這裡的除法 永遠會回傳浮點數
如果要取餘數或是整數的除法的話可以用以下方法
1 | iex(1)> div(10, 5) |
and
、or
和 not
,搭配這三個操作的第一個參數必須是 true
或 false
1 | iex(23)> true and 'a' |
Elixir 提供 ||
、 &&
和 !
||
運算子會回傳第一個不是 falsy 的值
&&
運算子,如果第一個表達式為 truthy 則會回傳第二個表達式,反之,回傳第一個表達式
1 | iex(1)> -20 || true |
比較運算子: ==
、 !=
、 ===
、 !==
、 <=
、 >=
、 <
和 >
。
1 | iex(1)> 1 > 2 |
為了嚴謹比較整數和浮點數,請使用 ===
1 | iex(5)> 2 == 2.0 |
Elixir 的一個重要特點是可以比較任意兩種型別;這在排序中特別有用。我們不需要記住排序順序,但還是要注意它的重要性
1 number < atom < reference < function < port < pid < tuple < map < list < bitstring這個特點可能會導致一些詭異但合乎語法,而您在其他語言中找不到的的比較運算
1
2
3
4 iex(1)> :hello > 999
true
iex(2)> {:hello, :world} > [1, 2, 3]
false