檢視原始碼 Kernel (Elixir v1.16.2)
Kernel
是 Elixir 的預設環境。
它主要包含
- 基本的語言原語,例如算術運算子、產生程序、資料類型處理等
- 控制流程和定義新功能(模組、函式等)的巨集
- 加強模式比對的防護檢查
您可以在 Elixir 程式碼中的任何地方呼叫 Kernel
函式和巨集,而無需使用 Kernel.
前綴,因為它們都已自動匯入。例如,在 IEx 中,您可以呼叫
iex> is_number(13)
true
如果您不想從 Kernel
匯入函式或巨集,請使用 :except
選項,然後依據元組列出函式/巨集
import Kernel, except: [if: 2, unless: 2]
請參閱 import/2
以取得更多關於匯入的資訊。
Elixir 也有始終匯入且無法略過的特殊形式。這些形式說明於 Kernel.SpecialForms
中。
標準函式庫
Kernel
提供 Elixir 標準函式庫建立其上的基本功能。建議探索標準函式庫以取得進階功能。以下是標準函式庫中模組的主要群組(此清單並非完整參考,請參閱文件側邊欄以取得所有條目)。
內建類型
下列模組處理 Elixir 內建資料類型
Atom
- 有名稱的文字常數(true
、false
和nil
是原子)Float
- 浮點精度的數字Function
- 透過fn/1
特殊形式建立的程式碼區塊參考Integer
- 整數(非分數)List
- 可變數量的元素集合(連結串列)Map
- 鍵值對集合Process
- 輕量級執行緒Port
- 與外部世界互動的機制Tuple
- 固定數量元素的集合
有兩種資料類型沒有附帶的模組
- Bitstring - 位元序列,使用
<<>>/1
建立。當位元數可以被 8 整除時,稱為二進位,可以使用 Erlang 的:binary
模組進行處理 - Reference - 執行時期系統中的唯一值,使用
make_ref/0
建立
資料類型
Elixir 也提供其他建立在上述類型上的資料類型。其中一些是
Date
- 給定日曆中的年-月-日
結構DateTime
- 給定日曆中的日期和時間,含時區Exception
- 從錯誤和意外情況引發的資料MapSet
- 無序的唯一元素集合NaiveDateTime
- 給定日曆中的日期和時間,不含時區Keyword
- 二元組清單,通常表示選用值Range
- 兩個整數之間的包含範圍Regex
- 正規表示式String
- 表示字元的 UTF-8 編碼二進位Time
- 給定日曆中的時:分:秒
結構URI
- 識別資源的 URI 表示Version
- 版本和需求的表示
系統模組
與底層系統介面的模組,例如
協定
協定為 Elixir 加入多型調度。它們是由資料類型可實作的合約。有關協定的更多資訊,請參閱 Protocol
。Elixir 在標準函式庫中提供下列協定
Collectable
- 將資料收集到資料類型中Enumerable
- 處理 Elixir 中的集合。Enum
模組提供用於處理集合的立即函式,Stream
模組提供延遲函式Inspect
- 將資料類型轉換成其程式語言表示List.Chars
- 將資料類型轉換成其外部世界表示,為字元清單 (非基於程式)String.Chars
- 將資料類型轉換成其外部世界表示,為字串 (非基於程式)
基於程序和以應用程式為中心的運作
下列模組建立在程序之上,以提供並行性、容錯性等功能。
Agent
- 封裝可變狀態的程序Application
- 用於啟動、停止和設定應用程式的函式GenServer
- 一般性的客戶端-伺服器 APIRegistry
- 基於程序的鍵值儲存Supervisor
- 負責啟動、監督和關閉其他程序的程序Task
- 執行運算的程序Task.Supervisor
- 專門用於管理任務的監督者
支援文件
在側邊欄的「頁面」區段中,您會找到更詳細說明 Elixir 語意和行為的教學課程、指南和參考文件。這些文件包括
- 相容性和已棄用 - 列出每個 Elixir 版本與 Erlang/OTP、發行架構之間的相容性;列出所有已棄用的函式、棄用時間和替代方案
- 函式庫指南 - 編寫函式庫的一般指南、反模式和規則
- 命名慣例 - Elixir 程式碼的命名慣例
- 運算子參考 - 列出所有 Elixir 運算子及其優先順序
- 模式和守衛 - 模式、守衛和擴充套件的簡介
- 語法參考 - 語言語法參考
- 類型規格參考 - 類型和函式規格,包括類型清單
- Unicode 語法 - 概述 Elixir 對 Unicode 的支援
守衛
此模組包含 Elixir 開發人員使用的內建守衛。它們是一組預定義的函式和巨集,用於擴充模式配對,通常在 when
運算子之後呼叫。例如
def drive(%User{age: age}) when age >= 16 do
...
end
只有當使用者的年齡大於或等於 16 歲時,才會呼叫上述子句。守衛還支援使用 and
和 or
連接多個條件。如果所有守衛表達式都評估為 true
,則整個守衛為真。可以在 模式和守衛 頁面中找到更完整的守衛簡介。
真值和假值
除了布林值 true
和 false
之外,Elixir 還有一個「真值」或「假值」的概念。
- 當一個值既不是
false
也不是nil
時,它就是真值 - 當一個值是
false
或nil
時,它就是假值
Elixir 有函數,例如 and/2
,僅適用於布林值,但也有函數適用於這些真值/假值,例如 &&/2
和 !/1
。
結構比較
此模組中的函數執行結構比較。這允許使用比較運算子比較不同的資料類型
iex> 1 < :an_atom
true
這是可能的,因此 Elixir 開發人員可以建立集合,例如字典和有序集合,其中儲存混合的資料類型。為了了解這一點的重要性,讓我們討論在軟體中找到的兩種比較類型:結構和語義。
結構表示我們正在比較基礎資料結構,而且我們通常希望這些運算能盡可能快,因為它用於支援語言中的多種演算法和資料結構。語義比較關注每個資料類型代表的內容。例如,從語義上來說,比較 時間
和 日期
沒有意義。
顯示結構比較和語義比較之間差異的一個範例是字串:「alien」排序小於「office」("alien" < "office"
),但「álien」大於「office」。這是因為 <
比較形成字串的基礎位元組。如果您正在進行字母順序排列,您可能希望「álien」也出現在「office」之前。
這表示Elixir 中的比較是結構性的,其目標是盡可能有效地比較資料類型,以建立彈性和高效能的資料結構。此區別對於提供排序的函數特別重要,例如 >/2
、</2
、>=/2
、<=/2
、min/2
和 max/2
。例如
~D[2017-03-31] > ~D[2017-04-01]
會回傳 true
,因為結構比較會比較 :day
欄位在 :month
或 :year
之前。為了執行語義比較,相關的資料類型提供 compare/2
函式,例如 Date.compare/2
iex> Date.compare(~D[2017-03-31], ~D[2017-04-01])
:lt
或者,你可以使用 Enum
模組中的函式來排序或計算最大值/最小值
iex> Enum.sort([~D[2017-03-31], ~D[2017-04-01]], Date)
[~D[2017-03-31], ~D[2017-04-01]]
iex> Enum.max([~D[2017-03-31], ~D[2017-04-01]], Date)
~D[2017-04-01]
第二個參數正是用於語義比較的模組。保持這個區別很重要,因為如果預設使用語義比較來實作資料結構和演算法,它們可能會變慢好幾個數量級!
最後,請注意有一個整體的結構排序順序,稱為「術語排序」,定義如下。提供此順序作為參考用途,Elixir 開發人員不需要死記硬背。
術語排序
number < atom < reference < function < port < pid < tuple < map < list < bitstring
比較兩個不同類型的數字(數字可以是整數或浮點數)時,除非使用的比較運算子是 ===/2
或 !==
,否則會轉換為精度較高的類型。浮點數會被視為比整數更精確,除非浮點數分別大於/小於 +/-9007199254740992.0,此時浮點數的所有有效數字都在小數點的左側。這種行為存在,以便大數的比較保持傳遞性。
集合類型使用以下規則進行比較
- 依大小比較元組,然後逐一比較元素。
- 依大小比較映射,然後依據術語順序比較鍵,再依據鍵順序比較值。在映射鍵順序的特定情況中,整數永遠被視為小於浮點數。
- 逐一比較清單。
- 逐位元組比較位元串,不完整的位元組逐位元比較。
- 使用字串值逐個碼點比較原子。
範例
我們可以使用 !/1
函式兩次來檢查值的真偽。
真值
iex> !!true
true
iex> !!5
true
iex> !![1,2]
true
iex> !!"foo"
true
假值(只有兩個)
iex> !!false
false
iex> !!nil
false
內嵌
本模組中描述的某些函式會由 Elixir 編譯器內嵌到 :erlang
模組中對應的 Erlang 對應函式中。這些函式在 Erlang 領域中稱為 BIF(內建內部函式),它們表現出有趣的特性,因為其中一些可以在防護中使用,而另一些則用於編譯器最佳化。
當擷取函式時,可以看到大多數內嵌函式的效果
iex> &Kernel.is_atom/1
&:erlang.is_atom/1
這些函式將在其文件檔中明確標示為「由編譯器內嵌」。
摘要
防護
算術乘法運算子。
算術正一元運算子。
算術加法運算子。
算術負一元運算子。
算術減法運算子。
算術除法運算子。
不等於運算子。
嚴格不等於運算子。
小於運算子。
小於或等於運算子。
等於運算子。如果兩個項相等,則傳回 true
。
嚴格等於運算子。
大於運算子。
大於或等於運算子。
傳回一個整數或浮點數,為 number
的算術絕對值。
嚴格布林「且」運算子。
從 start
處擷取 binary
的部分,大小為 size
。
傳回一個整數,為 bitstring
的位元大小。
傳回包含 bitstring
所需的位元組數。
傳回大於或等於 number
的最小整數。
執行整數除法。
取得 tuple
中從 0 開始的 index
處的元素。
傳回小於或等於 number
的最大整數。
傳回清單的頭部。如果清單為空,則引發 ArgumentError
。
成員運算子。
如果 term
是原子,則傳回 true
;否則傳回 false
。
如果 term
是二進位,則傳回 true
;否則傳回 false
。
如果 term
是位元串(包括二進位),則傳回 true
;否則傳回 false
。
如果 term
是原子 true
或原子 false
(即布林值),則傳回 true
;否則傳回 false
。
如果 term
是例外,則傳回 true
;否則傳回 false
。
如果 term
是 name
的例外,則傳回 true
;否則傳回 false
。
如果 term
是浮點數,則傳回 true
;否則傳回 false
。
如果 term
是函數,則傳回 true
;否則傳回 false
。
如果 term
是可以套用 arity
個數參數的函數,則傳回 true
;否則傳回 false
。
如果 term
是整數,則傳回 true
;否則傳回 false
。
如果 term
是包含零個或多個元素的清單,則傳回 true
;否則傳回 false
。
如果 term
是映射,則傳回 true
;否則傳回 false
。
如果 key
是 map
中的鍵,則傳回 true
;否則傳回 false
。
如果 term
是 nil
,則傳回 true
,否則傳回 false
。
如果 term
是整數或浮點數,則傳回 true
;否則傳回 false
。
如果 term
是 PID(程序識別碼),則傳回 true
;否則傳回 false
。
如果 term
是埠識別碼,則傳回 true
;否則傳回 false
。
如果 term
是參考,則傳回 true
;否則傳回 false
。
如果 term
是結構,傳回 true
;否則傳回 false
。
如果 term
是 name
的結構,傳回 true
;否則傳回 false
。
如果 term
是元組,傳回 true
;否則傳回 false
。
傳回 list
的長度。
傳回映射的大小。
傳回表示本機節點名稱的原子。如果節點未啟動,則傳回 :nonode@nohost
。
傳回指定引數所在節點。引數可以是 PID、參考或埠。如果本機節點未啟動,則傳回 :nonode@nohost
。
嚴格布林「not」運算子。
嚴格布林「or」運算子。
計算整數除法的餘數。
將數字四捨五入到最接近的整數。
傳回呼叫處理程序的 PID(處理程序識別碼)。
傳回清單的尾部。如果清單為空,則引發 ArgumentError
。
傳回 number
的整數部分。
傳回元組的大小。
函數
布林「and」運算子。
次方運算子。
清單串接運算子。串接適當的清單和術語,傳回清單。
清單減法運算子。移除左清單中每個元素在右清單中第一次出現的元素。
建立完整切片範圍 0..-1//1
。
建立從 first
到 last
的範圍。
建立從 first
到 last
且步長為 step
的範圍。
布林「not」運算子。
二進位串接運算子。串接兩個二進位。
基於文字的比對運算子。比對 left
上的術語與 right
上的正規表示法或字串。
模組屬性單元運算子。
在引用中使用時,標記給定的別名不應進行衛生處理。這表示在擴展巨集時,將會擴展別名。
使用引數清單 args
呼叫給定的匿名函數 fun
。
使用引數清單 args
呼叫 module
中的給定函數。
傳回從範圍開始的偏移量到範圍結束的偏移量所給定的二進位資料。
傳回從偏移量 start
開始且具有給定 size
的二進位資料。
傳回給定內容的繫結,作為關鍵字清單。
定義具有給定名稱和主體的公開函數。
定義委派至另一個模組的函數。
定義例外。
產生適合用於防護表達式的巨集。
產生適合用於防護表達式的私人巨集。
定義給定協定的實作。
定義具有給定名稱和主體的公開巨集。
定義具有給定名稱和主體的私人巨集。
定義具有給定內容的名稱模組。
讓目前模組中的給定定義可覆寫。
定義具有給定名稱和主體的私人函數。
定義結構。
解構兩個清單,將右邊清單中的每個術語指定給左邊清單中的相符術語。
停止執行呼叫程序,並提供指定的理由。
如果 module
已載入,且包含具有指定 arity
的公開 function
,則傳回 true
,否則傳回 false
。
透過指定的 path
取得值,並更新巢狀資料結構。
取得值,並更新巢狀結構。
從巢狀結構取得值。
提供 if/2
巨集。
根據 Inspect
協定檢查指定的參數。第二個參數是包含控制檢查的選項的關鍵字清單。
如果 module
已載入,且包含具有指定 arity
的公開 macro
,則傳回 true
,否則傳回 false
。
傳回幾乎唯一的參考。
一個便利巨集,用於檢查右側(表達式)是否與左側(模式)相符。
根據結構比較傳回兩個指定項中較大的項。
根據結構比較傳回兩個指定項中較小的項。
透過指定的 path
從巢狀結構中彈出一個鍵。
從指定的巢狀結構中彈出一個鍵。
將 value
放入 tuple
中指定的以 0 為基底的 index
。
透過指定的 path
在巢狀結構中放入一個值。
在巢狀結構中放入一個值。
引發例外狀況。
引發例外狀況。
引發例外狀況,並保留先前的堆疊追蹤。
引發例外狀況,並保留先前的堆疊追蹤。
傳送訊息至指定的 dest
,並傳回訊息。
處理字元清單的 sigil ~C
。
處理字元清單的 sigil ~c
。
處理日期的 sigil ~D
。
處理原始日期時間的 sigil ~N
。
處理正規表達式的 sigil ~r
。
處理字串的 sigil ~S
。
處理字串的 sigil ~s
。
處理時間的 sigil ~T
。
處理 sigil ~U
以建立 UTC DateTime
。
處理字詞清單的 sigil ~W
。
處理字詞清單的 sigil ~w
。
執行指定的函式並傳回其 PID。
從指定的 模組
執行指定的函式 函式
,傳遞指定的 引數
,並傳回其 PID。
執行指定的函式,將其連結至目前的程序,並傳回其 PID。
從指定的 模組
執行指定的函式 函式
,傳遞指定的 引數
,將其連結至目前的程序,並傳回其 PID。
執行指定的函式,監控其執行,並傳回其 PID 和監控參考。
執行指定的模組和函式,傳遞指定的引數,監控其執行,並傳回其 PID 和監控參考。
建立並更新結構。
類似於 struct/2
,但會檢查金鑰的有效性。
將第一個引數 值
傳遞至第二個引數(函式 函式
),並傳回 值
本身。
將第一個引數 值
傳遞至第二個引數(函式 函式
),並傳回呼叫 函式
的結果。
從函式中非區域性傳回。
根據 List.Chars
協定,將指定的項目轉換為字元清單。
根據 String.Chars
協定將參數轉換為字串。
提供 unless
巨集。
透過指定的 路徑
更新巢狀結構。
更新巢狀結構中的鍵。
在目前內容中使用指定的模組。
標記指定的變數不應衛生化。
管道運算子。
布林「或」運算子。
防護
@spec integer() * integer() :: integer()
@spec float() * float() :: float()
@spec integer() * float() :: float()
@spec float() * integer() :: float()
算術乘法運算子。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> 1 * 2
2
算術正一元運算子。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> +1
1
@spec integer() + integer() :: integer()
@spec float() + float() :: float()
@spec integer() + float() :: float()
@spec float() + integer() :: float()
算術加法運算子。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> 1 + 2
3
@spec -0 :: 0
@spec -pos_integer() :: neg_integer()
@spec -neg_integer() :: pos_integer()
@spec -float() :: float()
算術負一元運算子。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> -2
-2
@spec integer() - integer() :: integer()
@spec float() - float() :: float()
@spec integer() - float() :: float()
@spec float() - integer() :: float()
算術減法運算子。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> 1 - 2
-1
算術除法運算子。
結果永遠是浮點數。如果您想要整數除法或餘數,請使用 div/2
和 rem/2
。
如果 right
為 0 或 0.0,會引發 ArithmeticError
。
允許在防護測試中使用。由編譯器內嵌。
範例
1 / 2
#=> 0.5
-3.0 / 2.0
#=> -1.5
5 / 1
#=> 5.0
7 / 0
** (ArithmeticError) bad argument in arithmetic expression
不等於運算子。
如果兩個項不等於,會傳回 true
。
此運算子將 1 和 1.0 視為相等。對於比對比較,請改用 !==/2
。
此運算子執行結構比較,其中所有 Elixir 項都可以彼此比較。請參閱 「結構比較」區段 以取得更多資訊。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> 1 != 2
true
iex> 1 != 1.0
false
嚴格不等於運算子。
如果兩個項不等於,會傳回 true
。請參閱 ===/2
以取得「不等於」的定義。
此運算子執行結構比較,其中所有 Elixir 項都可以彼此比較。請參閱 「結構比較」區段 以取得更多資訊。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> 1 !== 2
true
iex> 1 !== 1.0
true
小於運算子。
如果 left
小於 right
,則傳回 true
。
此運算子執行結構比較,其中所有 Elixir 項都可以彼此比較。請參閱 「結構比較」區段 以取得更多資訊。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> 1 < 2
true
小於或等於運算子。
如果 left
小於或等於 right
,則傳回 true
。
此運算子執行結構比較,其中所有 Elixir 項都可以彼此比較。請參閱 「結構比較」區段 以取得更多資訊。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> 1 <= 2
true
等於運算子。如果兩個項相等,則傳回 true
。
這個運算子將 1 和 1.0 視為相等。對於更嚴格的語意,請改用 ===/2
。
此運算子執行結構比較,其中所有 Elixir 項都可以彼此比較。請參閱 「結構比較」區段 以取得更多資訊。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> 1 == 2
false
iex> 1 == 1.0
true
嚴格等於運算子。
如果兩個詞項完全相等,則傳回 true
。
只有當詞項具有相同值且屬於相同類型時,才將其視為完全相等。例如,1 == 1.0
傳回 true
,但由於它們屬於不同的類型,1 === 1.0
傳回 false
。
此運算子執行結構比較,其中所有 Elixir 項都可以彼此比較。請參閱 「結構比較」區段 以取得更多資訊。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> 1 === 2
false
iex> 1 === 1.0
false
大於運算子。
如果 left
大於 right
,則傳回 true
。
此運算子執行結構比較,其中所有 Elixir 項都可以彼此比較。請參閱 「結構比較」區段 以取得更多資訊。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> 1 > 2
false
大於或等於運算子。
如果 left
大於或等於 right
,則傳回 true
。
此運算子執行結構比較,其中所有 Elixir 項都可以彼此比較。請參閱 「結構比較」區段 以取得更多資訊。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> 1 >= 2
false
傳回一個整數或浮點數,為 number
的算術絕對值。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> abs(-3.33)
3.33
iex> abs(-3)
3
嚴格布林「且」運算子。
如果 left
為 false
,則傳回 false
;否則傳回 right
。
由於會短路,因此只需要 left
參數為布林值。如果 left
參數不是布林值,則會引發 BadBooleanError
例外。
允許在防護測試中使用。
範例
iex> true and false
false
iex> true and "yay!"
"yay!"
iex> "yay!" and true
** (BadBooleanError) expected a boolean on left-side of "and", got: "yay!"
@spec binary_part(binary(), non_neg_integer(), integer()) :: binary()
從 start
處擷取 binary
的部分,大小為 size
。
如果 start
或 size
參照二進位資料以外的任何方式,則會引發 ArgumentError
例外。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> binary_part("foo", 1, 2)
"oo"
負的 size
可用於擷取在 start
位元組之前的位元組
iex> binary_part("Hello", 5, -3)
"llo"
當大小超出二進制範圍時,會引發 ArgumentError
binary_part("Hello", 0, 10)
** (ArgumentError) argument error
@spec bit_size(bitstring()) :: non_neg_integer()
傳回一個整數,為 bitstring
的位元大小。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> bit_size(<<433::16, 3::3>>)
19
iex> bit_size(<<1, 2, 3>>)
24
@spec byte_size(bitstring()) :: non_neg_integer()
傳回包含 bitstring
所需的位元組數。
亦即,如果 bitstring
中的位元數無法被 8 整除,產生的位元組數會無條件進位。此操作會在固定時間內發生。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> byte_size(<<433::16, 3::3>>)
3
iex> byte_size(<<1, 2, 3>>)
3
傳回大於或等於 number
的最小整數。
如果您想對其他小數位執行無條件進位操作,請改用 Float.ceil/2
。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> ceil(10)
10
iex> ceil(10.1)
11
iex> ceil(-10.1)
-10
@spec div(integer(), neg_integer() | pos_integer()) :: integer()
執行整數除法。
如果其中一個參數不是整數,或 divisor
為 0
,會引發 ArithmeticError
例外。
div/2
執行截尾整數除法。這表示結果會無條件進位到 0。
如果您想執行取整數除法(無條件進位到負無限大),請改用 Integer.floor_div/2
。
允許在防護測試中使用。由編譯器內嵌。
範例
div(5, 2)
#=> 2
div(6, -4)
#=> -1
div(-99, 2)
#=> -49
div(100, 0)
** (ArithmeticError) bad argument in arithmetic expression
@spec elem(tuple(), non_neg_integer()) :: term()
取得 tuple
中從 0 開始的 index
處的元素。
當索引為負數或超出元組元素範圍時,會引發 ArgumentError
。
允許在防護測試中使用。由編譯器內嵌。
範例
tuple = {:foo, :bar, 3}
elem(tuple, 1)
#=> :bar
elem({}, 0)
** (ArgumentError) argument error
elem({:foo, :bar}, 2)
** (ArgumentError) argument error
傳回小於或等於 number
的最大整數。
如果您想對其他小數位執行取整操作,請改用 Float.floor/2
。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> floor(10)
10
iex> floor(9.7)
9
iex> floor(-9.7)
-10
@spec hd(nonempty_maybe_improper_list(elem, any())) :: elem when elem: term()
傳回清單的頭部。如果清單為空,則引發 ArgumentError
。
串列的頭部是其第一個元素。
它適用於不當串列。
允許在防護測試中使用。由編譯器內嵌。
範例
hd([1, 2, 3, 4])
#=> 1
hd([1 | 2])
#=> 1
給予它一個空清單會引發
hd([])
** (ArgumentError) argument error
成員運算子。
檢查左手邊的元素是否為右手邊集合的成員。
範例
iex> x = 1
iex> x in [1, 2, 3]
true
這個運算子(它是一個巨集)僅僅轉換為對 Enum.member?/2
的呼叫。上面的範例會轉換為
Enum.member?([1, 2, 3], x)
Elixir 也支援 left not in right
,它會評估為 not(left in right)
iex> x = 1
iex> x not in [1, 2, 3]
false
守衛
in/2
運算子(以及 not in
)可以在守衛子句中使用,只要右手邊是一個範圍或一個清單。
如果右手邊是一個清單,Elixir 會將運算子擴充為一個有效的守衛表達式,它需要檢查每個值。例如
when x in [1, 2, 3]
轉換為
when x === 1 or x === 2 or x === 3
然而,這個建構對於大型清單而言會很沒有效率。在這種情況下,最好停止使用守衛,並使用一個更適當的資料結構,例如 MapSet
。
如果右手邊是一個範圍,會執行一個更有效的比較檢查。例如
when x in 1..1000
大致轉換為
when x >= 1 and x <= 1000
AST 考量
left not in right
會被編譯器解析為 AST
{:not, _, [{:in, _, [left, right]}]}
這與 not(left in right)
的 AST 相同。
此外,Macro.to_string/2
和 Code.format_string!/2
會將這個 AST 的所有出現轉換為 left not in right
。
如果 term
是原子,則傳回 true
;否則傳回 false
。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> is_atom(false)
true
iex> is_atom(:name)
true
iex> is_atom(AnAtom)
true
iex> is_atom("true")
false
如果 term
是二進位,則傳回 true
;否則傳回 false
。
二進制總是包含一個完整的位元組數。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> is_binary("foo")
true
iex> is_binary(<<1::3>>)
false
如果 term
是位元串(包括二進位),則傳回 true
;否則傳回 false
。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> is_bitstring("foo")
true
iex> is_bitstring(<<1::3>>)
true
如果 term
是原子 true
或原子 false
(即布林值),則傳回 true
;否則傳回 false
。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> is_boolean(false)
true
iex> is_boolean(true)
true
iex> is_boolean(:test)
false
如果 term
是例外,則傳回 true
;否則傳回 false
。
允許在防護測試中使用。
範例
iex> is_exception(%RuntimeError{})
true
iex> is_exception(%{})
false
如果 term
是 name
的例外,則傳回 true
;否則傳回 false
。
允許在防護測試中使用。
範例
iex> is_exception(%RuntimeError{}, RuntimeError)
true
iex> is_exception(%RuntimeError{}, Macro.Env)
false
如果 term
是浮點數,則傳回 true
;否則傳回 false
。
允許在防護測試中使用。由編譯器內嵌。
如果 term
是函數,則傳回 true
;否則傳回 false
。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> is_function(fn x -> x + x end)
true
iex> is_function("not a function")
false
@spec is_function(term(), non_neg_integer()) :: boolean()
如果 term
是可以套用 arity
個數參數的函數,則傳回 true
;否則傳回 false
。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> is_function(fn x -> x * 2 end, 1)
true
iex> is_function(fn x -> x * 2 end, 2)
false
如果 term
是整數,則傳回 true
;否則傳回 false
。
允許在防護測試中使用。由編譯器內嵌。
如果 term
是包含零個或多個元素的清單,則傳回 true
;否則傳回 false
。
允許在防護測試中使用。由編譯器內嵌。
如果 term
是映射,則傳回 true
;否則傳回 false
。
允許在防護測試中使用。由編譯器內嵌。
如果 key
是 map
中的鍵,則傳回 true
;否則傳回 false
。
如果第一個元素不是映射,它會引發 BadMapError
。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> is_map_key(%{a: "foo", b: "bar"}, :a)
true
iex> is_map_key(%{a: "foo", b: "bar"}, :c)
false
如果 term
是 nil
,則傳回 true
,否則傳回 false
。
允許在防護子句中使用。
範例
iex> is_nil(1)
false
iex> is_nil(nil)
true
如果 term
是整數或浮點數,則傳回 true
;否則傳回 false
。
允許在防護測試中使用。由編譯器內嵌。
如果 term
是 PID(程序識別碼),則傳回 true
;否則傳回 false
。
允許在防護測試中使用。由編譯器內嵌。
如果 term
是埠識別碼,則傳回 true
;否則傳回 false
。
允許在防護測試中使用。由編譯器內嵌。
如果 term
是參考,則傳回 true
;否則傳回 false
。
允許在防護測試中使用。由編譯器內嵌。
如果 term
是結構,傳回 true
;否則傳回 false
。
允許在防護測試中使用。
範例
iex> is_struct(URI.parse("/"))
true
iex> is_struct(%{})
false
如果 term
是 name
的結構,傳回 true
;否則傳回 false
。
is_struct/2
沒有檢查 name
是否存在且為有效的結構。如果你想要此類驗證,你必須改為對結構進行模式比對,例如 match?(%URI{}, arg)
。
允許在防護測試中使用。
範例
iex> is_struct(URI.parse("/"), URI)
true
iex> is_struct(URI.parse("/"), Macro.Env)
false
如果 term
是元組,傳回 true
;否則傳回 false
。
允許在防護測試中使用。由編譯器內嵌。
@spec length(list()) :: non_neg_integer()
傳回 list
的長度。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> length([1, 2, 3, 4, 5, 6, 7, 8, 9])
9
@spec map_size(map()) :: non_neg_integer()
傳回映射的大小。
映射的大小是映射所包含的鍵值對數量。
此操作會在常數時間內發生。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> map_size(%{a: "foo", b: "bar"})
2
@spec node() :: node()
傳回表示本機節點名稱的原子。如果節點未啟動,則傳回 :nonode@nohost
。
允許在防護測試中使用。由編譯器內嵌。
傳回指定引數所在節點。引數可以是 PID、參考或埠。如果本機節點未啟動,則傳回 :nonode@nohost
。
允許在防護測試中使用。由編譯器內嵌。
@spec not true :: false
@spec not false :: true
嚴格布林「not」運算子。
value
必須是布林值;如果不是,則會引發 ArgumentError
例外。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> not false
true
嚴格布林「or」運算子。
如果 left
為 true
,則傳回 true
;否則傳回 right
。
由於會短路,因此只需要 left
參數為布林值。如果 left
參數不是布林值,則會引發 BadBooleanError
例外。
允許在防護測試中使用。
範例
iex> true or false
true
iex> false or 42
42
iex> 42 or false
** (BadBooleanError) expected a boolean on left-side of "or", got: 42
@spec rem(integer(), neg_integer() | pos_integer()) :: integer()
計算整數除法的餘數。
rem/2
使用截斷除法,表示結果永遠會有 dividend
的符號。
如果其中一個參數不是整數,或 divisor
為 0
,會引發 ArithmeticError
例外。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> rem(5, 2)
1
iex> rem(6, -4)
2
將數字四捨五入到最接近的整數。
如果數字與最近的兩個整數等距,則會四捨五入到遠離零的一方。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> round(5.6)
6
iex> round(5.2)
5
iex> round(-9.9)
-10
iex> round(-9)
-9
iex> round(2.5)
3
iex> round(-2.5)
-3
@spec self() :: pid()
傳回呼叫處理程序的 PID(處理程序識別碼)。
允許在防護子句中使用。由編譯器內嵌。
@spec tl(nonempty_maybe_improper_list(elem, last)) :: maybe_improper_list(elem, last) | last when elem: term(), last: term()
傳回清單的尾部。如果清單為空,則引發 ArgumentError
。
串列的尾部是沒有第一個元素的串列。
它適用於不當串列。
允許在防護測試中使用。由編譯器內嵌。
範例
tl([1, 2, 3, :go])
#=> [2, 3, :go]
tl([:one])
#=> []
tl([:a, :b | :improper_end])
#=> [:b | :improper_end]
tl([:a | %{b: 1}])
#=> %{b: 1}
給予它一個空清單會引發
tl([])
** (ArgumentError) argument error
傳回 number
的整數部分。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> trunc(5.4)
5
iex> trunc(-5.99)
-5
iex> trunc(-5)
-5
@spec tuple_size(tuple()) :: non_neg_integer()
傳回元組的大小。
此操作會在常數時間內發生。
允許在防護測試中使用。由編譯器內嵌。
範例
iex> tuple_size({:a, :b, :c})
3
函式
布林「and」運算子。
提供一個短路運算子,如果第一個表達式評估為真值(既不是 false
也不是 nil
),則只會評估並傳回第二個表達式。否則傳回第一個表達式。
不允許在防護子句中使用。
範例
iex> Enum.empty?([]) && Enum.empty?([])
true
iex> List.first([]) && true
nil
iex> Enum.empty?([]) && List.first([1])
1
iex> false && throw(:bad)
false
請注意,與 and/2
不同,此運算子接受任何表達式作為第一個引數,而不仅仅是布林值。
@spec integer() ** non_neg_integer() :: integer()
@spec integer() ** neg_integer() :: float()
@spec float() ** float() :: float()
@spec integer() ** float() :: float()
@spec float() ** integer() :: float()
次方運算子。
它需要兩個數字作為輸入。如果兩者都是整數,且右邊(指數
)也大於或等於 0,則結果也會是整數。否則,它會傳回浮點數。
範例
iex> 2 ** 2
4
iex> 2 ** -4
0.0625
iex> 2.0 ** 2
4.0
iex> 2 ** 2.0
4.0
@spec list() ++ term() :: maybe_improper_list()
清單串接運算子。串接適當的清單和術語,傳回清單。
a ++ b
的複雜度與 length(a)
成正比,因此避免重複附加到任意長度的清單,例如 list ++ [element]
。相反地,考慮透過 [element | rest]
加到前面,然後反轉。
如果 right
運算元不是正確的清單,它會傳回不正確的清單。如果 left
運算元不是正確的清單,它會引發 ArgumentError
。
由編譯器內嵌。
範例
iex> [1] ++ [2, 3]
[1, 2, 3]
iex> ~c"foo" ++ ~c"bar"
~c"foobar"
# a non-list on the right will return an improper list
# with said element at the end
iex> [1, 2] ++ 3
[1, 2 | 3]
iex> [1, 2] ++ {3, 4}
[1, 2 | {3, 4}]
# improper list on the right will return an improper list
iex> [1] ++ [2 | 3]
[1, 2 | 3]
++/2
運算子是右結合的,意即
iex> [1, 2, 3] -- [1] ++ [2]
[3]
等於
iex> [1, 2, 3] -- ([1] ++ [2])
[3]
清單減法運算子。移除左清單中每個元素在右清單中第一次出現的元素。
此函式經過最佳化,因此 a -- b
的複雜度與 length(a) * log(length(b))
成正比。另請參閱 Erlang 效率指南。
由編譯器內嵌。
範例
iex> [1, 2, 3] -- [1, 2]
[3]
iex> [1, 2, 3, 2, 1] -- [1, 2, 2]
[3, 1]
--/2
運算子是右結合的,意即
iex> [1, 2, 3] -- [2] -- [3]
[1, 3]
等於
iex> [1, 2, 3] -- ([2] -- [3])
[1, 3]
建立完整切片範圍 0..-1//1
。
此巨集傳回具有下列屬性的範圍
列舉時為空
用作
切片
時,會依原樣傳回切片元素
請參閱 ..///3
和 Range
模組,以取得更多資訊。
範例
iex> Enum.to_list(..)
[]
iex> String.slice("Hello world!", ..)
"Hello world!"
建立從 first
到 last
的範圍。
如果 first 小於 last,範圍將從 first 增加到 last。如果 first 等於 last,範圍將包含一個元素,也就是數字本身。
如果 first 大於 last,範圍將從 first 遞減到 last,儘管這種行為已被棄用。相反,建議使用 first..last//-1
明確列出步驟。
請參閱 Range
模組以取得更多資訊。
範例
iex> 0 in 1..3
false
iex> 2 in 1..3
true
iex> Enum.to_list(1..3)
[1, 2, 3]
建立從 first
到 last
且步長為 step
的範圍。
請參閱 Range
模組以取得更多資訊。
範例
iex> 0 in 1..3//1
false
iex> 2 in 1..3//1
true
iex> 2 in 1..3//2
false
iex> Enum.to_list(1..3//1)
[1, 2, 3]
iex> Enum.to_list(1..3//2)
[1, 3]
iex> Enum.to_list(3..1//-1)
[3, 2, 1]
iex> Enum.to_list(1..0//1)
[]
布林「not」運算子。
接收任何值(不只是布林值),如果 value
為 false
或 nil
,則傳回 true
;否則傳回 false
。
不允許在防護子句中使用。
範例
iex> !Enum.empty?([])
false
iex> !List.first([])
true
二進位串接運算子。串接兩個二進位。
如果其中一面不是二進位,則會引發 ArgumentError
。
範例
iex> "foo" <> "bar"
"foobar"
<>/2
算子也可以用於模式比對(和防護子句),只要左邊引數是文字二進位
iex> "foo" <> x = "foobar"
iex> x
"bar"
x <> "bar" = "foobar"
會導致 ArgumentError
例外狀況。
基於文字的比對運算子。比對 left
上的術語與 right
上的正規表示法或字串。
如果 right
是正規表示式,則如果 left
符合 right,則傳回 true
。
如果 right
是字串,則回傳 true
,如果 left
包含 right
。
範例
iex> "abcd" =~ ~r/c(d)/
true
iex> "abcd" =~ ~r/e/
false
iex> "abcd" =~ ~r//
true
iex> "abcd" =~ "bc"
true
iex> "abcd" =~ "ad"
false
iex> "abcd" =~ "abcd"
true
iex> "abcd" =~ ""
true
有關正則表示式的更多資訊,請查看 Regex
模組。
模組屬性單元運算子。
讀取和寫入目前模組中的屬性。
屬性的正規範例是註解模組實作 OTP 行為,例如 GenServer
defmodule MyServer do
@behaviour GenServer
# ... callbacks ...
end
預設情況下,Elixir 支援 Erlang 支援的所有模組屬性,但也可以使用自訂屬性
defmodule MyServer do
@my_data 13
IO.inspect(@my_data)
#=> 13
end
與 Erlang 不同,此類屬性預設不會儲存在模組中,因為在 Elixir 中,使用自訂屬性來儲存編譯時可用的暫時資料很常見。自訂屬性可以透過使用 Module.register_attribute/3
來設定為更接近 Erlang 的行為。
為模組屬性加上前綴
函式庫和架構應考慮為任何私有的模組屬性加上底線前綴,例如
@_my_data
,這樣程式碼完成工具就不會在建議和提示中顯示它們。
最後,請注意,屬性也可以在函式內讀取
defmodule MyServer do
@my_data 11
def first_data, do: @my_data
@my_data 13
def second_data, do: @my_data
end
MyServer.first_data()
#=> 11
MyServer.second_data()
#=> 13
重要的是要注意,讀取屬性會擷取其目前值的快照。換句話說,該值是在編譯時讀取的,而不是在執行時。查看 Module
模組以取得用於處理模組屬性的其他函式。
注意!同一個屬性的多重參照
如上所述,每次讀取模組屬性時,都會擷取其當前值的快照。因此,如果您將大型值儲存在模組屬性中(例如,將外部檔案嵌入到模組屬性中),則應避免多次參照同一個屬性。例如,請勿執行此操作
@files %{
example1: File.read!("lib/example1.data"),
example2: File.read!("lib/example2.data")
}
def example1, do: @files[:example1]
def example2, do: @files[:example2]
在上述範例中,每次參照 @files
都可能以完整的個別副本結束整個 @files
模組屬性。相反地,在私有函式中參照模組屬性一次
@files %{
example1: File.read!("lib/example1.data"),
example2: File.read!("lib/example2.data")
}
defp files(), do: @files
def example1, do: files()[:example1]
def example2, do: files()[:example2]
注意!編譯時期依賴項
請記住,即使在模組屬性中,對其他模組的參照也會產生對所述模組的編譯時期依賴項。
例如,採用此常見模式
@values [:foo, :bar, :baz]
def handle_arg(arg) when arg in @values do
...
end
雖然上述範例沒問題,但假設您在模組屬性中有實際的模組名稱,如下所示
@values [Foo, Bar, Baz]
def handle_arg(arg) when arg in @values do
...
end
上述程式碼將定義對模組 Foo
、Bar
和 Baz
的編譯時期依賴項,如果其中任何一個發生變更,目前的模組都必須重新編譯。在這種情況下,最好完全避免使用模組屬性
def handle_arg(arg) when arg in [Foo, Bar, Baz] do
...
end
在引用中使用時,標記給定的別名不應進行衛生處理。這表示在擴展巨集時,將會擴展別名。
查看 quote/2
以取得更多資訊。
使用引數清單 args
呼叫給定的匿名函數 fun
。
如果在編譯時期已知參數數量,請優先使用 fun.(arg_1, arg_2, ..., arg_n)
,因為它比 apply(fun, [arg_1, arg_2, ..., arg_n])
更清楚。
由編譯器內嵌。
範例
iex> apply(fn x -> x * 2 end, [2])
4
使用引數清單 args
呼叫 module
中的給定函數。
apply/3
用於呼叫函式,其中模組、函式名稱或參數是在執行時期動態定義的。因此,您無法使用 apply/3
呼叫巨集,只能呼叫函式。
如果在編譯時期已知參數數量和函式名稱,請優先使用 module.function(arg_1, arg_2, ..., arg_n)
,因為它比 apply(module, :function, [arg_1, arg_2, ..., arg_n])
更清楚。
apply/3
無法用於呼叫私有函式。
由編譯器內嵌。
範例
iex> apply(Enum, :reverse, [[1, 2, 3]])
[3, 2, 1]
傳回從範圍開始的偏移量到範圍結束的偏移量所給定的二進位資料。
如果範圍的開始或結束為負數,它們會根據二進位大小轉換為正數索引。例如,-1
表示二進位的最後一個位元組。
這類似於 binary_part/3
,但它適用於範圍,且不允許在防護中使用。
此函式適用於位元組。如需考量字元的切片操作,請參閱 String.slice/2
。
範例
iex> binary_slice("elixir", 0..5)
"elixir"
iex> binary_slice("elixir", 1..3)
"lix"
iex> binary_slice("elixir", 1..10)
"lixir"
iex> binary_slice("elixir", -4..-1)
"ixir"
iex> binary_slice("elixir", -4..6)
"ixir"
iex> binary_slice("elixir", -10..10)
"elixir"
對於 start > stop
的範圍,您需要明確標記它們為遞增
iex> binary_slice("elixir", 2..-1//1)
"ixir"
iex> binary_slice("elixir", 1..-2//1)
"lixi"
您可以使用 ../0
作為 0..-1//1
的捷徑,它會原樣傳回整個二進位
iex> binary_slice("elixir", ..)
"elixir"
步長可以是任何正數。例如,要取得二進位的每個 2 個字元
iex> binary_slice("elixir", 0..-1//2)
"eii"
如果第一個位置在字串結束之後或在範圍的最後一個位置之後,它會傳回一個空字串
iex> binary_slice("elixir", 10..3//1)
""
iex> binary_slice("elixir", -10..-7)
""
iex> binary_slice("a", 1..1500)
""
傳回從偏移量 start
開始且具有給定 size
的二進位資料。
這類似於 binary_part/3
,但如果 start + size
大於二進位大小,它會自動將其裁剪為二進位大小,而不是引發。與 binary_part/3
相反,此函式不允許在防護中使用。
此函式適用於位元組。如需考量字元的切片操作,請參閱 String.slice/3
。
範例
iex> binary_slice("elixir", 0, 6)
"elixir"
iex> binary_slice("elixir", 0, 5)
"elixi"
iex> binary_slice("elixir", 1, 4)
"lixi"
iex> binary_slice("elixir", 0, 10)
"elixir"
如果 start
為負數,它會根據二進位大小標準化並限制為 0
iex> binary_slice("elixir", -3, 10)
"xir"
iex> binary_slice("elixir", -10, 10)
"elixir"
如果 size
為零,會傳回一個空二進位
iex> binary_slice("elixir", 1, 0)
""
如果 start
大於或等於二進制大小,則會傳回一個空的二進制
iex> binary_slice("elixir", 10, 10)
""
傳回給定內容的繫結,作為關鍵字清單。
在傳回的結果中,金鑰是變數名稱,而值是對應的變數值。
如果給定的 context
是 nil
(預設為 nil),則會傳回目前 context 的繫結。
範例
iex> x = 1
iex> binding()
[x: 1]
iex> x = 2
iex> binding()
[x: 2]
iex> binding(:foo)
[]
iex> var!(x, :foo) = 1
1
iex> binding(:foo)
[x: 1]
偵錯給定的 code
。
dbg/2
可用於透過可設定的偵錯函式偵錯給定的 code
。它會傳回給定程式碼的結果。
範例
我們來呼叫 dbg/2
dbg(Atom.to_string(:debugging))
#=> "debugging"
它會傳回字串 "debugging"
,這是 Atom.to_string/1
呼叫的結果。此外,上述呼叫會印出
[my_file.ex:10: MyMod.my_fun/0]
Atom.to_string(:debugging) #=> "debugging"
預設的偵錯函式在處理管線時會印出額外的偵錯資訊。它會印出管線中每個「步驟」的值。
"Elixir is cool!"
|> String.trim_trailing("!")
|> String.split()
|> List.first()
|> dbg()
#=> "Elixir"
上述程式碼會印出
[my_file.ex:10: MyMod.my_fun/0]
"Elixir is cool!" #=> "Elixir is cool!"
|> String.trim_trailing("!") #=> "Elixir is cool"
|> String.split() #=> ["Elixir", "is", "cool"]
|> List.first() #=> "Elixir"
如果沒有參數,dbg()
會偵錯目前繫結的資訊。請參閱 binding/1
。
dbg
在 IEx 中
你可以啟用 IEx 以其 IEx.pry/0
後端取代 dbg
,方法是呼叫
$ iex --dbg pry
在這種情況下,dbg
會啟動一個 pry
會話,你可以在其中與 dbg
呼叫位置的目前環境中的匯入、別名和變數進行互動。
如果你在 IEx 中於管線的結尾 (使用 |>
) 呼叫 dbg
,你可以透過輸入「next」(或「n」) 來逐一檢視管線的每個步驟。
請注意,dbg
僅支援管線的逐步執行 (換句話說,它只能逐步執行它看到的程式碼)。對於一般的逐步執行,你可以使用 IEx.break!/4
設定中斷點。
如需更多資訊,請參閱 IEx 文件。
設定除錯函數
dbg/2
的其中一個好處是它的除錯邏輯是可設定的,允許工具以增強的行為來延伸 dbg
。例如,IEx
就會延伸 dbg
,並提供一個互動式外殼,讓你可以直接檢查和存取值。
除錯函數可以在編譯時透過 :elixir
應用程式的 :dbg_callback
鍵來設定。除錯函數必須是一個 {module, function, args}
元組。module
中的 function
函數會以三個參數呼叫,這些參數會附加到 args
除錯函數會在編譯時呼叫,而且它也必須回傳一個 AST。預期 AST 最後會回傳評估除錯表達式的結果。
以下是一個簡單的範例
defmodule MyMod do
def debug_fun(code, options, caller, device) do
quote do
result = unquote(code)
IO.inspect(unquote(device), result, label: unquote(Macro.to_string(code)))
end
end
end
設定除錯函數
# In config/config.exs
config :elixir, :dbg_callback, {MyMod, :debug_fun, [:stdio]}
預設除錯函數
預設情況下,我們使用的除錯函數是 Macro.dbg/3
。它只會將程式碼的資訊列印到標準輸出,並回傳評估 code
所回傳的值。options
用於控制項式的檢查方式。它們是 inspect/2
所接受的相同選項。
定義具有給定名稱和主體的公開函數。
範例
defmodule Foo do
def bar, do: :baz
end
Foo.bar()
#=> :baz
需要參數的函數可以定義如下
defmodule Foo do
def sum(a, b) do
a + b
end
end
在上面的範例中,定義了一個 sum/2
函數;此函數會接收兩個參數,並回傳它們的總和。
預設參數
\\
用於指定函數參數的預設值。例如
defmodule MyMath do
def multiply_by(number, factor \\ 2) do
number * factor
end
end
MyMath.multiply_by(4, 3)
#=> 12
MyMath.multiply_by(4)
#=> 8
編譯器會將此轉換為具有不同元數的函數,這裡 MyMath.multiply_by/1
和 MyMath.multiply_by/2
,表示傳遞或未傳遞具有預設值的參數的引數時的情況。
在定義具有預設引數以及多個明確宣告子句的函數時,您必須撰寫宣告預設值的函數標頭。例如
defmodule MyString do
def join(string1, string2 \\ nil, separator \\ " ")
def join(string1, nil, _separator) do
string1
end
def join(string1, string2, separator) do
string1 <> separator <> string2
end
end
請注意,\\
無法與匿名函數一起使用,因為它們只能有一個元數。
具有預設引數的關鍵字清單
包含許多引數的函數可以使用 Keyword
清單來分組並將屬性作為單一值傳遞,從而受益。
defmodule MyConfiguration do
@default_opts [storage: "local"]
def configure(resource, opts \\ []) do
opts = Keyword.merge(@default_opts, opts)
storage = opts[:storage]
# ...
end
end
使用 Map
和 Keyword
儲存許多引數的差異在於 Keyword
的金鑰
- 必須是原子
- 可以給予多次
- 已排序,由開發人員指定
函數名稱
Elixir 中的函數和變數名稱必須以底線或非大寫或標題格式的 Unicode 字母開頭。它們可以使用 Unicode 字母、數字和底線的順序繼續使用。它們可能以 ?
或 !
結尾。Elixir 的 命名慣例 建議以 snake_case
格式撰寫函數和變數名稱。
rescue
/catch
/after
/else
函數主體支援 rescue
、catch
、after
和 else
,就像 try/1
所做的那樣(稱為「隱式嘗試」)。例如,以下兩個函數是等效的
def convert(number) do
try do
String.to_integer(number)
rescue
e in ArgumentError -> {:error, e.message}
end
end
def convert(number) do
String.to_integer(number)
rescue
e in ArgumentError -> {:error, e.message}
end
定義委派至另一個模組的函數。
使用 defdelegate/2
定義的函數是公開的,並且可以從定義它們的模組外部呼叫,就像使用 def/2
定義它們一樣。因此,defdelegate/2
是關於擴充目前模組的公開 API。如果您想要呼叫在其他模組中定義的函數,而不使用其完整的模組名稱,那麼請使用 alias/2
來縮短模組名稱,或使用 import/2
來完全不使用模組名稱來呼叫函數。
委派僅適用於函數;不支援委派巨集。
查看 def/2
以了解有關命名和預設引數的規則。
選項
:to
- 要派送到的模組。:as
- 要在:to
中指定的目標上呼叫的函數。此參數是選用的,預設值為委派的名稱 (funs
)。
範例
defmodule MyList do
defdelegate reverse(list), to: Enum
defdelegate other_reverse(list), to: Enum, as: :reverse
end
MyList.reverse([1, 2, 3])
#=> [3, 2, 1]
MyList.other_reverse([1, 2, 3])
#=> [3, 2, 1]
定義例外。
例外狀況是結構,由實作 Exception
行為的模組所支援。 Exception
行為需要實作兩個函數
exception/1
- 接收傳遞給raise/2
的引數,並傳回例外狀況結構。預設實作接受合併到結構中的關鍵字引數集,或用作例外狀況訊息的字串。message/1
- 接收例外狀況結構,並且必須傳回其訊息。大多數例外狀況都有訊息欄位,預設是由此函數存取。但是,如果例外狀況沒有訊息欄位,則必須明確實作此函數。
由於例外狀況是結構,defstruct/1
支援的 API 也在 defexception/1
中提供。
引發例外狀況
引發例外狀況最常見的方式是透過 raise/2
defmodule MyAppError do
defexception [:message]
end
value = [:hello]
raise MyAppError,
message: "did not get what was expected, got: #{inspect(value)}"
在許多情況下,將預期的值傳遞給 raise/2
較為方便,並在 Exception.exception/1
回呼中產生訊息
defmodule MyAppError do
defexception [:message]
@impl true
def exception(value) do
msg = "did not get what was expected, got: #{inspect(value)}"
%MyAppError{message: msg}
end
end
raise MyAppError, value
上述範例顯示自訂例外狀況訊息的首選策略。
產生適合用於防護表達式的巨集。
如果定義使用警衛中不允許的運算式,它會在編譯時引發錯誤,否則會建立一個巨集,可以在警衛內或外使用。
請注意 Elixir 中的慣例是為所有回傳布林值的警衛加上 is_
前綴,例如 is_list/1
。但是,如果函式/巨集回傳布林值且不允許在警衛中使用,則不應加上前綴,並以問號結尾,例如 Keyword.keyword?/1
。
範例
defmodule Integer.Guards do
defguard is_even(value) when is_integer(value) and rem(value, 2) == 0
end
defmodule Collatz do
@moduledoc "Tools for working with the Collatz sequence."
import Integer.Guards
@doc "Determines the number of steps `n` takes to reach `1`."
# If this function never converges, please let me know what `n` you used.
def converge(n) when n > 0, do: step(n, 0)
defp step(1, step_count) do
step_count
end
defp step(n, step_count) when is_even(n) do
step(div(n, 2), step_count + 1)
end
defp step(n, step_count) do
step(3 * n + 1, step_count + 1)
end
end
產生適合用於防護表達式的私人巨集。
如果定義使用警衛中不允許的運算式,它會在編譯時引發錯誤,否則會建立一個私有巨集,可以在目前模組的警衛內或外使用。
類似於 defmacrop/2
,defguardp/1
必須在目前模組中使用前定義。
定義給定協定的實作。
有關更多資訊,請參閱 Protocol
模組。
定義具有給定名稱和主體的公開巨集。
巨集必須在使用之前定義。
查看 def/2
以了解有關命名和預設引數的規則。
範例
defmodule MyLogic do
defmacro unless(expr, opts) do
quote do
if !unquote(expr), unquote(opts)
end
end
end
require MyLogic
MyLogic.unless false do
IO.puts("It works")
end
定義具有給定名稱和主體的私人巨集。
私人巨集只能從定義它們的相同模組中存取。
私人巨集必須在使用之前定義。
請查看 defmacro/2
以取得更多資訊,並查看 def/2
以了解命名和預設引數的規則。
定義具有給定內容的名稱模組。
此巨集定義一個模組,其名稱為指定的 alias
,並具有指定的內容。它會傳回一個包含四個元素的元組
:module
- 模組名稱
- 模組的二進位內容
- 評估內容區塊的結果
範例
defmodule Number do
def one, do: 1
def two, do: 2
end
#=> {:module, Number, <<70, 79, 82, ...>>, {:two, 0}}
Number.one()
#=> 1
Number.two()
#=> 2
模組名稱和別名
模組名稱 (和別名) 必須以 ASCII 大寫字母開頭,其後可以接續任何 ASCII 字母、數字或底線。Elixir 的 命名慣例 建議使用 CamelCase
格式來撰寫模組名稱和別名。
您也可以使用原子作為模組名稱,儘管它們只能包含 ASCII 字元。
巢狀
在另一個模組中巢狀模組會影響巢狀模組的名稱
defmodule Foo do
defmodule Bar do
end
end
在上面的範例中,建立了兩個模組 - Foo
和 Foo.Bar
-。在巢狀時,Elixir 會自動建立一個別名到內部模組,讓第二個模組 Foo.Bar
可以使用 Bar
存取,在定義它的同一個詞彙範圍 (也就是 Foo
模組) 中。這只會在巢狀模組透過別名定義時發生。
如果 Foo.Bar
模組被移到別的地方,Foo
模組中對 Bar
的參照需要更新為完全限定名稱 (Foo.Bar
),或者必須在 Foo
模組中使用 alias/2
明確設定一個別名。
defmodule Foo.Bar do
# code
end
defmodule Foo do
alias Foo.Bar
# code here can refer to "Foo.Bar" as just "Bar"
end
動態名稱
Elixir 模組名稱可以動態產生。這在使用巨集時非常有用。例如,可以寫
defmodule Module.concat(["Foo", "Bar"]) do
# contents ...
end
只要傳遞給 defmodule/2
的第一個參數的運算式評估為原子,Elixir 就會接受任何模組名稱。請注意,當使用動態名稱時,Elixir 既不會將名稱巢狀到目前模組中,也不會自動設定別名。
保留的模組名稱
如果你嘗試定義一個已經存在的模組,你會收到一個警告,表示模組已被重新定義。
有一些模組是 Elixir 目前沒有實作的,但未來可能會實作。這些模組是保留的,定義它們會導致編譯錯誤
defmodule Any do
# code
end
** (CompileError) iex:1: module Any is reserved and cannot be defined
Elixir 保留以下模組名稱:Elixir
、Any
、BitString
、PID
和 Reference
。
讓目前模組中的給定定義可覆寫。
如果使用者使用相同名稱和元數定義新的函式或巨集,那麼可覆寫的函式或巨集就會被捨棄。否則,會使用原始定義。
覆寫的定義有可能具有與原始定義不同的可見性:公開函式可以被私有函式覆寫,反之亦然。
巨集無法覆寫為函式,反之亦然。
範例
defmodule DefaultMod do
defmacro __using__(_opts) do
quote do
def test(x, y) do
x + y
end
defoverridable test: 2
end
end
end
defmodule ChildMod do
use DefaultMod
def test(x, y) do
x * y + super(x, y)
end
end
如上方的範例所示,super
可用於呼叫預設實作。
免責聲明
小心使用
defoverridable
。如果您需要定義具有相同行為的多個模組,最好將預設實作移至呼叫方,並透過Code.ensure_loaded?/1
和function_exported?/3
檢查是否存在回呼。例如,在上方範例中,假設有一個呼叫
test/2
函式的模組。此模組可以定義如下defmodule CallsTest do def receives_module_and_calls_test(module, x, y) do if Code.ensure_loaded?(module) and function_exported?(module, :test, 2) do module.test(x, y) else x + y end end end
具有行為的範例
您也可以將行為傳遞給 defoverridable
,它會將行為中的所有回呼標記為可覆寫
defmodule Behaviour do
@callback test(number(), number()) :: number()
end
defmodule DefaultMod do
defmacro __using__(_opts) do
quote do
@behaviour Behaviour
def test(x, y) do
x + y
end
defoverridable Behaviour
end
end
end
defmodule ChildMod do
use DefaultMod
def test(x, y) do
x * y + super(x, y)
end
end
定義具有給定名稱和主體的私人函數。
私有函式只能從定義它們的模組內部存取。嘗試從定義模組外部存取私有函式會導致 UndefinedFunctionError
例外。
查看 def/2
以取得更多資訊。
範例
defmodule Foo do
def bar do
sum(1, 2)
end
defp sum(a, b), do: a + b
end
Foo.bar()
#=> 3
Foo.sum(1, 2)
** (UndefinedFunctionError) undefined function Foo.sum/2
定義協定。
有關更多資訊,請參閱 Protocol
模組。
定義結構。
結構是一個標記式映射,允許開發人員為金鑰提供預設值、用於多型分派的標記和編譯時斷言。有關結構的更多資訊,請查看 %/2
。
每個模組只能定義一個結構,因為結構與模組本身相關聯。呼叫 defstruct/1
也會定義一個 __struct__/0
函式,用於傳回結構本身。
範例
defmodule User do
defstruct name: nil, age: nil
end
結構欄位會在編譯時評估,這允許它們是動態的。在以下範例中,10 + 11
會在編譯時評估,而 age 欄位會儲存值 21
defmodule User do
defstruct name: nil, age: 10 + 11
end
fields
參數通常是一個關鍵字清單,其中欄位名稱為原子金鑰,預設值為對應的值。 defstruct/1
也支援將原子清單作為其參數:在這種情況下,清單中的原子將用作結構的欄位名稱,並且它們都預設為 nil
。
defmodule Post do
defstruct [:title, :content, :author]
end
使用 @doc
屬性為結構新增文件,就像函式一樣。
defmodule Post do
@doc "A post. The content should be valid Markdown."
defstruct [:title, :content, :author]
end
衍生
雖然結構是映射,但預設情況下結構不會實作任何為映射實作的協定。例如,嘗試使用協定與 User
結構會導致錯誤
john = %User{name: "John"}
MyProtocol.call(john)
** (Protocol.UndefinedError) protocol MyProtocol not implemented for %User{...}
defstruct/1
允許協定實作被衍生。這可透過在呼叫 defstruct/1
之前定義一個 @derive
屬性作為清單來完成
defmodule User do
@derive MyProtocol
defstruct name: nil, age: nil
end
MyProtocol.call(john) # it works!
一個常見的範例是 @derive
Inspect
協定,以在列印結構時隱藏特定欄位
defmodule User do
@derive {Inspect, only: :name}
defstruct name: nil, age: nil
end
對於 @derive
中的每個協定,Elixir 會聲明協定已為 Any
實作。如果 Any
實作定義一個 __deriving__/3
回呼,則會呼叫回呼,且它應該定義實作模組。否則,會自動衍生一個僅指向 Any
實作的實作。有關 __deriving__/3
回呼的更多資訊,請參閱 Protocol.derive/3
。
強制鍵
在建構結構時,Elixir 會自動保證所有鍵都屬於結構
%User{name: "john", unknown: :key}
** (KeyError) key :unknown not found in: %User{age: 21, name: nil}
Elixir 也允許開發人員強制要求在建構結構時必須總是提供特定鍵
defmodule User do
@enforce_keys [:name]
defstruct name: nil, age: 10 + 11
end
現在嘗試在沒有 name 鍵的情況下建構結構會失敗
%User{age: 21}
** (ArgumentError) the following keys must also be given when building struct User: [:name]
請記住 @enforce_keys
是在建構結構時協助開發人員的簡單編譯時保證。它不會在更新時強制執行,也不會提供任何類型的值驗證。
類型
建議為結構定義類型。依慣例,此類型的名稱為 t
。若要在類型內定義結構,請使用結構文字語法
defmodule User do
defstruct name: "John", age: 25
@type t :: %__MODULE__{name: String.t(), age: non_neg_integer}
end
建議僅在定義結構的類型時使用結構語法。在參照另一個結構時,最好使用 User.t()
而不是 %User{}
。
未包含在 %User{}
中的結構欄位類型預設為 term()
(請參閱 term/0
)。
其內部結構對本機模組為私有的結構(不應允許對其進行模式比對或直接存取其欄位)應使用 @opaque
屬性。其內部結構為公開的結構應使用 @type
。
解構兩個清單,將右邊清單中的每個術語指定給左邊清單中的相符術語。
與透過 =
進行模式比對不同,如果左右清單的大小不符,解構只會停止,而不會引發錯誤。
範例
iex> destructure([x, y, z], [1, 2, 3, 4, 5])
iex> {x, y, z}
{1, 2, 3}
在上述範例中,即使右清單的項目比左清單多,解構仍可正常運作。如果右清單較小,則其餘元素只會設為 nil
iex> destructure([x, y, z], [1])
iex> {x, y, z}
{1, nil, nil}
左方支援您會在比對的左方使用的任何表達式
x = 1
destructure([^x, y, z], [1, 2, 3])
上述範例只有在 x
與右清單中的第一個值相符時才會運作。否則,它會引發 MatchError
(就像 =
算子會做的一樣)。
停止執行呼叫程序,並提供指定的理由。
由於評估此函式會導致程序終止,因此它沒有傳回值。
由編譯器內嵌。
範例
當程序到達其終點時,預設會以 :normal
原因退出。如果您想終止程序,但不想發出任何失敗訊號,也可以明確呼叫 exit/1
exit(:normal)
如果發生錯誤,您也可以使用 exit/1
搭配不同的原因
exit(:seems_bad)
如果退出原因不是 :normal
,則連結到已退出程序的所有程序都會崩潰(除非它們正在攔截退出)。
OTP 退出
OTP 使用退出來判斷程序是否異常退出。下列退出視為「正常」
exit(:normal)
exit(:shutdown)
exit({:shutdown, term})
使用任何其他原因退出視為異常,並視為崩潰。這表示預設的監督行為會啟動,錯誤報告會發出,等等。
許多不同的地方都仰賴此行為。例如,ExUnit
在退出測試程序時使用 exit(:shutdown)
來發出訊號,讓連結的程序、監督樹等等有禮貌地關閉。
CLI 退出
建立在上述提到的退出訊號之上,如果由命令列啟動的程序退出時具有上述三個原因中的任何一個,其退出視為正常,而作業系統程序將以狀態 0 退出。
不過,可以透過呼叫來自訂作業系統退出訊號
exit({:shutdown, integer})
這將導致作業系統程序以 integer
給定的狀態退出,同時發出訊號給所有連結的 Erlang 程序,讓它們有禮貌地關閉。
任何其他退出原因將導致作業系統程序以狀態 1
退出,並導致連結的 Erlang 程序崩潰。
如果 module
已載入,且包含具有指定 arity
的公開 function
,則傳回 true
,否則傳回 false
。
請注意,此函式不會在模組未載入的情況下載入模組。請查看 Code.ensure_loaded/1
以取得更多資訊。
由編譯器內嵌。
範例
iex> function_exported?(Enum, :map, 2)
true
iex> function_exported?(Enum, :map, 10)
false
iex> function_exported?(List, :to_string, 1)
true
透過指定的 path
取得值,並更新巢狀資料結構。
這類似於 get_and_update_in/3
,但路徑是透過巨集萃取,而不是傳遞清單。例如
get_and_update_in(opts[:foo][:bar], &{&1, &1 + 1})
等於
get_and_update_in(opts, [:foo, :bar], &{&1, &1 + 1})
這也適用於巢狀結構和 struct.path.to.value
指定路徑的方式
get_and_update_in(struct.foo.bar, &{&1, &1 + 1})
請注意,為了讓此巨集運作,此巨集必須始終可見完整的路徑。請參閱下列的「路徑」區段。
範例
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> get_and_update_in(users["john"].age, &{&1, &1 + 1})
{27, %{"john" => %{age: 28}, "meg" => %{age: 23}}}
路徑
路徑可以從變數、區域或遠端呼叫開始,且必須後接一個或多個
foo[bar]
- 存取foo
中的bar
鍵;如果foo
為 nil,則傳回nil
foo.bar
- 存取映射/結構欄位;如果欄位不存在,則會引發錯誤
以下是部分有效路徑
users["john"][:age]
users["john"].age
User.all()["john"].age
all_users()["john"].age
以下是部分無效路徑
# Does a remote call after the initial value
users["john"].do_something(arg1, arg2)
# Does not access any key or field
users
@spec get_and_update_in( structure, keys, (term() | nil -> {current_value, new_value} | :pop) ) :: {current_value, new_structure :: structure} when structure: Access.t(), keys: [any(), ...], current_value: Access.value(), new_value: Access.value()
取得值,並更新巢狀結構。
data
為巢狀結構(即實作 Access
行為的映射、關鍵字清單或結構)。
fun
參數接收 key
的值(如果 key
不存在,則為 nil
),且必須傳回下列值之一
二元組
{current_value, new_value}
。在此情況下,current_value
為已擷取的值,在傳回前可能已進行運算。new_value
為要儲存在key
下的新值。:pop
,表示應從結構中移除key
下的目前值,並傳回該值。
此函式使用 Access
模組來根據所提供的 keys
遍歷結構,除非 key
為函式,其詳細資訊將在後續章節說明。
範例
此函式在需要同時擷取目前值(或根據目前值計算得出的結果)並更新該值時會很有用。例如,它可用於讀取使用者的目前年齡,並在一次傳遞中將其增加一歲
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> get_and_update_in(users, ["john", :age], &{&1, &1 + 1})
{27, %{"john" => %{age: 28}, "meg" => %{age: 23}}}
請注意,提供給匿名函式的目前值可能是 nil
。如果任何中間值為 nil,則會引發
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> get_and_update_in(users, ["jane", :age], &{&1, &1 + 1})
** (ArgumentError) could not put/update key :age on a nil value
函式作為鍵
如果鍵為函式,則會呼叫該函式並傳遞三個參數
- 操作 (
:get_and_update
) - 要存取的資料
- 要接著呼叫的函式
這表示 get_and_update_in/3
可以延伸提供自訂查詢。缺點是函式無法儲存在存取的資料結構中。
當其中一個鍵是函式時,會呼叫該函式。在以下範例中,我們使用函式取得清單中所有年齡並增加其值
iex> users = [%{name: "john", age: 27}, %{name: "meg", age: 23}]
iex> all = fn :get_and_update, data, next ->
...> data |> Enum.map(next) |> Enum.unzip()
...> end
iex> get_and_update_in(users, [all, :age], &{&1, &1 + 1})
{[27, 23], [%{name: "john", age: 28}, %{name: "meg", age: 24}]}
如果呼叫函式前的先前值為 nil
,函式會收到 nil
作為值,並必須適當地處理它(可能是失敗或提供合理的預設值)。
Access
模組附帶許多便利的存取器函式,例如上面定義的 all
匿名函式。請參閱 Access.all/0
、Access.key/2
等作為範例。
從巢狀結構取得值。
使用 Access
模組根據指定的 keys
遍歷結構,除非 key
是函式,這將在後面的章節中詳細說明。
請注意,如果指定的鍵都不是函式,則很少有理由使用 get_in
而不用使用 []
編寫「一般」Elixir 程式碼。
範例
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> get_in(users, ["john", :age])
27
iex> # Equivalent to:
iex> users["john"][:age]
27
get_in/2
也可以使用 Access
模組中的存取器來遍歷更複雜的資料結構。例如,這裡我們使用 Access.all/0
來遍歷清單
iex> users = [%{name: "john", age: 27}, %{name: "meg", age: 23}]
iex> get_in(users, [Access.all(), :age])
[27, 23]
如果任何組件傳回 nil
,則會傳回 nil
,而 get_in/2
也不會再進一步遍歷
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> get_in(users, ["unknown", :age])
nil
iex> # Equivalent to:
iex> users["unknown"][:age]
nil
iex> users = nil
iex> get_in(users, [Access.all(), :age])
nil
或者,如果您需要存取複雜的資料結構,可以使用模式比對
case users do
%{"john" => %{age: age}} -> age
_ -> default_value
end
函式作為鍵
如果傳給 get_in/2
的鍵是函式,則會呼叫該函式,並傳遞三個引數
- 操作 (
:get
) - 要存取的資料
- 要接著呼叫的函式
這表示 get_in/2
可以延伸提供自訂查詢。這正是前一章節中 Access.all/0
鍵的行為方式。例如,我們可以手動實作此類遍歷,如下所示
iex> users = [%{name: "john", age: 27}, %{name: "meg", age: 23}]
iex> all = fn :get, data, next -> Enum.map(data, next) end
iex> get_in(users, [all, :age])
[27, 23]
Access
模組附帶許多便利的存取器函式。請參閱 Access.all/0
、Access.key/2
等作為範例。
使用結構
預設情況下,結構不會實作此函式所需的 Access
行為。因此,您無法執行此操作
get_in(some_struct, [:some_key, :nested_key])
好消息是結構有預先定義的形狀。因此,您可以改寫為
some_struct.some_key.nested_key
如果 some_key
有可能傳回 nil,您可以隨時使用模式比對來提供巢狀結構處理
case some_struct do
%{some_key: %{nested_key: value}} -> value
%{} -> nil
end
提供 if/2
巨集。
此巨集預期第一個引數為條件,第二個引數為關鍵字清單。
單行範例
if(foo, do: bar)
在上述範例中,如果 foo
評估為真值(既不是 false
也不是 nil
),將傳回 bar
。否則,將傳回 nil
。
可以指定 else
選項來指定相反的結果
if(foo, do: bar, else: baz)
區塊範例
也可以將區塊傳遞給 if/2
巨集。上述第一個範例將轉換為
if foo do
bar
end
請注意 do
-end
會變成區隔符號。第二個範例將轉換為
if foo do
bar
else
baz
end
若要比較兩個以上的子句,必須使用 cond/1
巨集。
根據 Inspect
協定檢查指定的參數。第二個參數是包含控制檢查的選項的關鍵字清單。
選項
inspect/2
接受選項清單,這些選項會在內部轉換為 Inspect.Opts
結構。查看 Inspect.Opts
的文件以查看支援的選項。
範例
iex> inspect(:foo)
":foo"
iex> inspect([1, 2, 3, 4, 5], limit: 3)
"[1, 2, 3, ...]"
iex> inspect([1, 2, 3], pretty: true, width: 0)
"[1,\n 2,\n 3]"
iex> inspect("olá" <> <<0>>)
"<<111, 108, 195, 161, 0>>"
iex> inspect("olá" <> <<0>>, binaries: :as_strings)
"\"olá\\0\""
iex> inspect("olá", binaries: :as_binaries)
"<<111, 108, 195, 161>>"
iex> inspect(~c"bar")
"~c\"bar\""
iex> inspect([0 | ~c"bar"])
"[0, 98, 97, 114]"
iex> inspect(100, base: :octal)
"0o144"
iex> inspect(100, base: :hex)
"0x64"
請注意,Inspect
協定不一定要傳回 Elixir 項目的有效表示。在這種情況下,檢查結果必須以 #
開頭。例如,檢查函式將傳回
inspect(fn a, b -> a + b end)
#=> #Function<...>
可以衍生 Inspect
協定以隱藏結構中的某些欄位,讓它們不會顯示在記錄、檢查等中。請參閱 Inspect
協定文件中的「衍生」區段以取得更多資訊。
如果 module
已載入,且包含具有指定 arity
的公開 macro
,則傳回 true
,否則傳回 false
。
請注意,此函式不會在模組未載入的情況下載入模組。請查看 Code.ensure_loaded/1
以取得更多資訊。
如果 module
是 Erlang 模組(相對於 Elixir 模組),此函數總是傳回 false
。
範例
iex> macro_exported?(Kernel, :use, 2)
true
iex> macro_exported?(:erlang, :abs, 1)
false
@spec make_ref() :: reference()
傳回幾乎唯一的參考。
傳回的參照會在約 2^82 次呼叫後再次出現;因此,它對於實際用途而言足夠唯一。
由編譯器內嵌。
範例
make_ref()
#=> #Reference<0.0.0.135>
一個便利巨集,用於檢查右側(表達式)是否與左側(模式)相符。
範例
iex> match?(1, 1)
true
iex> match?({1, _}, {1, 2})
true
iex> map = %{a: 1, b: 2}
iex> match?(%{a: _}, map)
true
iex> a = 1
iex> match?(^a, 1)
true
match?/2
在過濾或尋找可列舉中的值時非常有用
iex> list = [a: 1, b: 2, a: 3]
iex> Enum.filter(list, &match?({:a, _}, &1))
[a: 1, a: 3]
守衛子句也可以提供給比對
iex> list = [a: 1, b: 2, a: 3]
iex> Enum.filter(list, &match?({:a, x} when x < 2, &1))
[a: 1]
在比對中指派的變數將無法在函數呼叫之外使用(與使用 =
算子進行一般樣式比對不同)
iex> match?(_x, 1)
true
iex> binding()
[]
值與樣式
請記住,pin 運算子比對的是值,而不是樣式。將變數傳遞為樣式將總是傳回 true
,並會產生警告,指出變數未被使用
# don't do this
pattern = %{a: :a}
match?(pattern, %{b: :b})
同樣地,將表達式移出樣式可能無法再保留其語意。例如
match?([_ | _], [1, 2, 3])
#=> true
pattern = [_ | _]
match?(pattern, [1, 2, 3])
** (CompileError) invalid use of _. _ can only be used inside patterns to ignore values and cannot be used in expressions. Make sure you are inside a pattern or change it accordingly
另一個範例是,作為樣式的映射執行子集比對,但一旦指派給變數後就不會執行。
match?(%{x: 1}, %{x: 1, y: 2})
#=> true
attrs = %{x: 1}
match?(^attrs, %{x: 1, y: 2})
#=> false
pin 運算子會使用 ===/2
檢查值是否相等,而樣式在比對映射、清單等時有自己的規則。此類行為並非特定於 match?/2
。下列程式碼也會擲回例外
attrs = %{x: 1}
^attrs = %{x: 1, y: 2}
#=> (MatchError) no match of right hand side value: %{x: 1, y: 2}
根據結構比較傳回兩個指定項中較大的項。
如果項相等,則傳回第一個項。
此運算子執行結構比較,其中所有 Elixir 項都可以彼此比較。請參閱 「結構比較」區段 以取得更多資訊。
由編譯器內嵌。
範例
iex> max(1, 2)
2
iex> max(:a, :b)
:b
根據結構比較傳回兩個指定項中較小的項。
如果項相等,則傳回第一個項。
此運算子執行結構比較,其中所有 Elixir 項都可以彼此比較。請參閱 「結構比較」區段 以取得更多資訊。
由編譯器內嵌。
範例
iex> min(1, 2)
1
iex> min("foo", "bar")
"bar"
透過指定的 path
從巢狀結構中彈出一個鍵。
這類似於 pop_in/2
,不同之處在於路徑是透過巨集萃取,而不是傳遞清單。例如
pop_in(opts[:foo][:bar])
等於
pop_in(opts, [:foo, :bar])
請注意,為了讓這個巨集運作,這個巨集必須始終可見完整的路徑。如需有關支援路徑表達式的更多資訊,請查看 get_and_update_in/2
文件。
範例
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> pop_in(users["john"][:age])
{27, %{"john" => %{}, "meg" => %{age: 23}}}
iex> users = %{john: %{age: 27}, meg: %{age: 23}}
iex> pop_in(users.john[:age])
{27, %{john: %{}, meg: %{age: 23}}}
如果任何項目傳回 nil
,其金鑰會被移除,且刪除會被視為成功。
@spec pop_in(data, [Access.get_and_update_fun(term(), data) | term(), ...]) :: {term(), data} when data: Access.container()
從指定的巢狀結構中彈出一個鍵。
使用 Access
協定根據指定的 keys
遍歷結構,除非 key
是函式。如果金鑰是函式,它會如 get_and_update_in/3
中所指定的方式被呼叫。
範例
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> pop_in(users, ["john", :age])
{27, %{"john" => %{}, "meg" => %{age: 23}}}
如果任何項目傳回 nil
,其金鑰會被移除,且刪除會被視為成功。
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> pop_in(users, ["jane", :age])
{nil, %{"john" => %{age: 27}, "meg" => %{age: 23}}}
@spec put_elem(tuple(), non_neg_integer(), term()) :: tuple()
將 value
放入 tuple
中指定的以 0 為基底的 index
。
由編譯器內嵌。
範例
iex> tuple = {:foo, :bar, 3}
iex> put_elem(tuple, 0, :baz)
{:baz, :bar, 3}
透過指定的 path
在巢狀結構中放入一個值。
這類似於 put_in/3
,不同之處在於路徑是透過巨集萃取,而不是傳遞清單。例如
put_in(opts[:foo][:bar], :baz)
等於
put_in(opts, [:foo, :bar], :baz)
這也適用於巢狀結構和 struct.path.to.value
指定路徑的方式
put_in(struct.foo.bar, :baz)
請注意,為了讓這個巨集運作,這個巨集必須始終可見完整的路徑。如需有關支援路徑表達式的更多資訊,請查看 get_and_update_in/2
文件。
範例
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> put_in(users["john"][:age], 28)
%{"john" => %{age: 28}, "meg" => %{age: 23}}
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> put_in(users["john"].age, 28)
%{"john" => %{age: 28}, "meg" => %{age: 23}}
在巢狀結構中放入一個值。
使用 Access
模組根據指定的 keys
遍歷結構,除非 key
是函式。如果金鑰是函式,它會如 get_and_update_in/3
中所指定的方式被呼叫。
範例
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> put_in(users, ["john", :age], 28)
%{"john" => %{age: 28}, "meg" => %{age: 23}}
如果任何中間值為 nil,它會引發
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> put_in(users, ["jane", :age], "oops")
** (ArgumentError) could not put/update key :age on a nil value
引發例外狀況。
如果 message
是字串,它會引發 RuntimeError
例外狀況。
如果 message
是原子,它僅呼叫 raise/2
,其中原子作為第一個參數,[]
作為第二個參數。
如果 message
是例外結構,則照樣引發。
如果 message
是其他任何內容,raise
會失敗,並引發 ArgumentError
例外。
範例
iex> raise "oops"
** (RuntimeError) oops
try do
1 + :foo
rescue
x in [ArithmeticError] ->
IO.puts("that was expected")
raise x
end
引發例外狀況。
在給定的參數(必須是模組名稱,例如 ArgumentError
或 RuntimeError
)上呼叫 exception/1
函式,傳遞 attributes
以擷取例外結構。
包含呼叫 defexception/1
巨集的任何模組都會自動實作 raise/2
預期的 Exception.exception/1
回呼。如需更多資訊,請參閱 defexception/1
。
範例
iex> raise(ArgumentError, "Sample")
** (ArgumentError) Sample
引發例外狀況,並保留先前的堆疊追蹤。
作用類似 raise/1
,但不會產生新的堆疊追蹤。
請注意,可以在 catch/rescue 內部使用 __STACKTRACE__
來擷取目前的堆疊追蹤。
範例
try do
raise "oops"
rescue
exception ->
reraise exception, __STACKTRACE__
end
引發例外狀況,並保留先前的堆疊追蹤。
reraise/3
的作用類似 reraise/2
,但它會將參數傳遞給 exception/1
函式,如 raise/2
中所述。
範例
try do
raise "oops"
rescue
exception ->
reraise WrapperError, [exception: exception], __STACKTRACE__
end
@spec send(dest :: Process.dest(), message) :: message when message: any()
傳送訊息至指定的 dest
,並傳回訊息。
dest
可以是遠端或本機 PID、本機埠、本機註冊名稱,或格式為 {registered_name, node}
的組態,用於另一個節點上的註冊名稱。
由編譯器內嵌。
範例
iex> send(self(), :hello)
:hello
處理字元清單的 sigil ~C
。
它會傳回一個沒有內插和跳脫字元的字元清單,但封閉符號字元本身的跳脫除外。
字元清單是一個整數清單,其中所有整數都是有效的碼點。以下三個表達式是等效的
~C"foo\n"
[?f, ?o, ?o, ?\\, ?n]
[102, 111, 111, 92, 110]
在實務上,字元清單主要用於特定情境,例如與不接受二進位資料作為參數的舊版 Erlang 函式庫介接。
範例
iex> ~C(foo)
~c"foo"
iex> ~C(f#{o}o)
~c"f\#{o}o"
iex> ~C(foo\n)
~c"foo\\n"
處理字元清單的 sigil ~c
。
它會傳回一個字元清單,取消跳脫字元並取代內插。
字元清單是一個整數清單,其中所有整數都是有效的碼點。以下三個表達式是等效的
~c"foo"
[?f, ?o, ?o]
[102, 111, 111]
在實務上,字元清單主要用於特定情境,例如與不接受二進位資料作為參數的舊版 Erlang 函式庫介接。
範例
iex> ~c(foo)
~c"foo"
iex> ~c(f#{:o}o)
~c"foo"
iex> ~c(f\#{:o}o)
~c"f\#{:o}o"
只有當所有碼點都在 ASCII 範圍內時,清單才會列印為 ~c
符號。
iex> ~c"hełło"
[104, 101, 322, 322, 111]
iex> [104, 101, 108, 108, 111]
~c"hello"
請參閱 Inspect.Opts
以取得更多資訊。
處理日期的 sigil ~D
。
預設情況下,此符號使用內建 Calendar.ISO
,它要求日期以 ISO8601 格式撰寫
~D[yyyy-mm-dd]
例如
~D[2015-01-13]
如果您使用的是其他日曆,只要在表示法後面加上一個空格和日曆名稱,就可以使用任何表示法
~D[SOME-REPRESENTATION My.Alternative.Calendar]
小寫的 ~d
變體不存在,因為內插和跳脫字元對日期符號沒有用。
可以在 Date
模組中找到更多有關日期的資訊。
範例
iex> ~D[2015-01-13]
~D[2015-01-13]
處理原始日期時間的 sigil ~N
。
預設情況下,此符號使用內建 Calendar.ISO
,它要求未指定時區的日期時間以 ISO8601 格式撰寫
~N[yyyy-mm-dd hh:mm:ss]
~N[yyyy-mm-dd hh:mm:ss.ssssss]
~N[yyyy-mm-ddThh:mm:ss.ssssss]
例如
~N[2015-01-13 13:00:07]
~N[2015-01-13T13:00:07.123]
如果您使用的是其他日曆,只要在表示法後面加上一個空格和日曆名稱,就可以使用任何表示法
~N[SOME-REPRESENTATION My.Alternative.Calendar]
小寫的 ~n
變體不存在,因為內插和跳脫字元對日期時間符號沒有用。
可以在 NaiveDateTime
模組中找到更多有關未指定時區的日期時間的資訊。
範例
iex> ~N[2015-01-13 13:00:07]
~N[2015-01-13 13:00:07]
iex> ~N[2015-01-13T13:00:07.001]
~N[2015-01-13 13:00:07.001]
處理正規表達式的 sigil ~r
。
它回傳一個正規表示式模式,取消字元的跳脫並替換內插。
更多關於正規表示式的資訊可以在 Regex
模組中找到。
範例
iex> Regex.match?(~r/foo/, "foo")
true
iex> Regex.match?(~r/a#{:b}c/, "abc")
true
雖然 ~r
標記允許使用括號和中括號作為分隔符號,但建議使用 "
或 /
以避免跳脫與保留正規表示式字元衝突。
處理字串的 sigil ~S
。
它回傳一個沒有內插和跳脫字元的字串,除了封閉標記字元本身的跳脫。
範例
iex> ~S(foo)
"foo"
iex> ~S(f#{o}o)
"f\#{o}o"
iex> ~S(\o/)
"\\o/"
然而,如果你想在字串中重複使用標記字元本身,你需要跳脫它
iex> ~S((\))
"()"
處理字串的 sigil ~s
。
它回傳一個字串,就像它是雙引號字串,取消字元的跳脫並替換內插。
範例
iex> ~s(foo)
"foo"
iex> ~s(f#{:o}o)
"foo"
iex> ~s(f\#{:o}o)
"f\#{:o}o"
處理時間的 sigil ~T
。
預設情況下,此標記使用內建的 Calendar.ISO
,它需要以 ISO8601 格式撰寫時間
~T[hh:mm:ss]
~T[hh:mm:ss.ssssss]
例如
~T[13:00:07]
~T[13:00:07.123]
如果您使用的是其他日曆,只要在表示法後面加上一個空格和日曆名稱,就可以使用任何表示法
~T[SOME-REPRESENTATION My.Alternative.Calendar]
小寫的 ~t
變體不存在,因為內插和跳脫字元對時間標記沒有用。
更多關於時間的資訊可以在 Time
模組中找到。
範例
iex> ~T[13:00:07]
~T[13:00:07]
iex> ~T[13:00:07.001]
~T[13:00:07.001]
處理 sigil ~U
以建立 UTC DateTime
。
預設情況下,此印記使用內建的 Calendar.ISO
,它要求 UTC 日期時間以 ISO8601 格式撰寫
~U[yyyy-mm-dd hh:mm:ssZ]
~U[yyyy-mm-dd hh:mm:ss.ssssssZ]
~U[yyyy-mm-ddThh:mm:ss.ssssss+00:00]
例如
~U[2015-01-13 13:00:07Z]
~U[2015-01-13T13:00:07.123+00:00]
如果您使用的是其他日曆,只要在表示法後面加上一個空格和日曆名稱,就可以使用任何表示法
~U[SOME-REPRESENTATION My.Alternative.Calendar]
給定的 datetime_string
必須包含標示為 UTC 的「Z」或「00:00」偏移量,否則會引發錯誤。
小寫 ~u
變體不存在,因為插補和跳脫字元對日期時間印記沒有用處。
可以在 DateTime
模組中找到有關日期時間的更多資訊。
範例
iex> ~U[2015-01-13 13:00:07Z]
~U[2015-01-13 13:00:07Z]
iex> ~U[2015-01-13T13:00:07.001+00:00]
~U[2015-01-13 13:00:07.001Z]
處理字詞清單的 sigil ~W
。
它會傳回一個由空白分隔的「字詞」清單,不包含插補和跳脫字元,但印記字元本身的跳脫除外。
修飾詞
s
:清單中的字詞為字串(預設)a
:清單中的字詞為原子c
:清單中的字詞為字元清單
範例
iex> ~W(foo #{bar} baz)
["foo", "\#{bar}", "baz"]
處理字詞清單的 sigil ~w
。
它會傳回一個由空白分隔的「字詞」清單。每個字詞都會進行字元取消跳脫和插補。
修飾詞
s
:清單中的字詞為字串(預設)a
:清單中的字詞為原子c
:清單中的字詞為字元清單
範例
iex> ~w(foo #{:bar} baz)
["foo", "bar", "baz"]
iex> ~w(foo #{" bar baz "})
["foo", "bar", "baz"]
iex> ~w(--source test/enum_test.exs)
["--source", "test/enum_test.exs"]
iex> ~w(foo bar baz)a
[:foo, :bar, :baz]
iex> ~w(foo bar baz)c
[~c"foo", ~c"bar", ~c"baz"]
執行指定的函式並傳回其 PID。
通常開發人員不會使用 spawn
函式,而是使用抽象化,例如 Task
、GenServer
和 Agent
,這些抽象化建立在 spawn
之上,可以更方便地執行內省和除錯。
請查看 Process
模組以取得更多與程序相關的函式。
匿名函式接收 0 個引數,且可以傳回任何值。
由編譯器內嵌。
範例
current = self()
child = spawn(fn -> send(current, {self(), 1 + 2}) end)
receive do
{^child, 3} -> IO.puts("Received 3 back")
end
從指定的 模組
執行指定的函式 函式
,傳遞指定的 引數
,並傳回其 PID。
通常開發人員不會使用 spawn
函式,而是使用抽象化,例如 Task
、GenServer
和 Agent
,這些抽象化建立在 spawn
之上,可以更方便地執行內省和除錯。
請查看 Process
模組以取得更多與程序相關的函式。
由編譯器內嵌。
範例
spawn(SomeModule, :function, [1, 2, 3])
執行指定的函式,將其連結至目前的程序,並傳回其 PID。
通常開發人員不會使用 spawn
函式,而是使用抽象化,例如 Task
、GenServer
和 Agent
,這些抽象化建立在 spawn
之上,可以更方便地執行內省和除錯。
查看 Process
模組以取得更多與處理相關的函式。如需連結的更多資訊,請查看 Process.link/1
。
匿名函式接收 0 個引數,且可以傳回任何值。
由編譯器內嵌。
範例
current = self()
child = spawn_link(fn -> send(current, {self(), 1 + 2}) end)
receive do
{^child, 3} -> IO.puts("Received 3 back")
end
從指定的 模組
執行指定的函式 函式
,傳遞指定的 引數
,將其連結至目前的程序,並傳回其 PID。
通常開發人員不會使用 spawn
函式,而是使用抽象化,例如 Task
、GenServer
和 Agent
,這些抽象化建立在 spawn
之上,可以更方便地執行內省和除錯。
查看 Process
模組以取得更多與處理相關的函式。如需連結的更多資訊,請查看 Process.link/1
。
由編譯器內嵌。
範例
spawn_link(SomeModule, :function, [1, 2, 3])
執行指定的函式,監控其執行,並傳回其 PID 和監控參考。
通常開發人員不會使用 spawn
函式,而是使用抽象化,例如 Task
、GenServer
和 Agent
,這些抽象化建立在 spawn
之上,可以更方便地執行內省和除錯。
請查看 Process
模組以取得更多與程序相關的函式。
匿名函式接收 0 個引數,且可以傳回任何值。
由編譯器內嵌。
範例
current = self()
spawn_monitor(fn -> send(current, {self(), 1 + 2}) end)
執行指定的模組和函式,傳遞指定的引數,監控其執行,並傳回其 PID 和監控參考。
通常開發人員不會使用 spawn
函式,而是使用抽象化,例如 Task
、GenServer
和 Agent
,這些抽象化建立在 spawn
之上,可以更方便地執行內省和除錯。
請查看 Process
模組以取得更多與程序相關的函式。
由編譯器內嵌。
範例
spawn_monitor(SomeModule, :function, [1, 2, 3])
@spec struct(module() | struct(), Enumerable.t()) :: struct()
建立並更新結構。
struct
參數可能是原子(定義 defstruct
)或 struct
本身。第二個參數是任何 Enumerable
,在列舉期間會發出二元組(鍵值對)。
Enumerable
中不存在於 struct 中的鍵會自動捨棄。請注意,鍵必須是原子,因為在定義 struct 時只允許原子。如果 Enumerable
中有重複的鍵,將會採用最後的項目(與 Map.new/1
相同的行為)。
此函式對於動態建立和更新 struct 很實用,也適用於將 map 轉換為 struct;在後者的情況下,僅在 map 中插入適當的 :__struct__
欄位可能不夠,而應改用 struct/2
。
範例
defmodule User do
defstruct name: "john"
end
struct(User)
#=> %User{name: "john"}
opts = [name: "meg"]
user = struct(User, opts)
#=> %User{name: "meg"}
struct(user, unknown: "value")
#=> %User{name: "meg"}
struct(User, %{name: "meg"})
#=> %User{name: "meg"}
# String keys are ignored
struct(User, %{"name" => "meg"})
#=> %User{name: "john"}
@spec struct!(module() | struct(), Enumerable.t()) :: struct()
類似於 struct/2
,但會檢查金鑰的有效性。
函式 struct!/2
模擬 struct 的編譯時間行為。這表示
在建立 struct 時,例如
struct!(SomeStruct, key: :value)
,等同於%SomeStruct{key: :value}
,因此此函式會檢查每個給定的鍵值是否屬於 struct。如果 struct 透過@enforce_keys
強制執行任何鍵,也會強制執行這些鍵;在更新 struct 時,例如
struct!(%SomeStruct{}, key: :value)
,等同於%SomeStruct{struct | key: :value}
,因此此函式會檢查每個給定的鍵值是否屬於 struct。然而,更新 struct 不會強制執行鍵,因為鍵只會在建立時強制執行;
將第一個引數 值
傳遞至第二個引數(函式 函式
),並傳回 值
本身。
可用於在管線中執行同步副作用,使用 |>/2
運算子。
範例
iex> tap(1, fn x -> x + 1 end)
1
最常見的用法是在管道中,使用 |>/2
算子。例如,假設您想要檢查資料結構的一部分。您可以寫
%{a: 1}
|> Map.update!(:a, & &1 + 2)
|> tap(&IO.inspect(&1.a))
|> Map.update!(:a, & &1 * 2)
將第一個引數 值
傳遞至第二個引數(函式 函式
),並傳回呼叫 函式
的結果。
換句話說,它呼叫函式 fun
,並將 value
作為引數,並傳回其結果。
最常見的用法是在管道中,使用 |>/2
算子,讓您可以將值傳遞給函式,而函式的第一個引數不在此值中。
範例
iex> 1 |> then(fn x -> x * 2 end)
2
iex> 1 |> then(fn x -> Enum.drop(["a", "b", "c"], x) end)
["b", "c"]
從函式中非區域性傳回。
通常不建議使用 throw/1
,因為它允許函式跳脫其常規執行流程,這可能會讓程式碼更難閱讀。此外,所有拋出的值都必須由 try/catch
捕捉。有關更多資訊,請參閱 try/1
。
由編譯器內嵌。
根據 List.Chars
協定,將指定的項目轉換為字元清單。
範例
iex> to_charlist(:foo)
~c"foo"
根據 String.Chars
協定將參數轉換為字串。
這是字串內插時呼叫的函式。
範例
iex> to_string(:foo)
"foo"
提供 unless
巨集。
如果 condition
評估為假值 (false
或 nil
),這個巨集會評估並傳回作為第二個引數傳入的 do
區塊。否則,如果存在 else
區塊,它會傳回該區塊的值;如果不存在,則傳回 nil
。
另請參閱 if/2
。
範例
iex> unless(Enum.empty?([]), do: "Hello")
nil
iex> unless(Enum.empty?([1, 2, 3]), do: "Hello")
"Hello"
iex> unless Enum.sum([2, 2]) == 5 do
...> "Math still works"
...> else
...> "Math is broken"
...> end
"Math still works"
透過指定的 路徑
更新巢狀結構。
這類似於 update_in/3
,但路徑是透過巨集萃取,而不是傳遞清單。例如
update_in(opts[:foo][:bar], &(&1 + 1))
等於
update_in(opts, [:foo, :bar], &(&1 + 1))
這也適用於巢狀結構和 struct.path.to.value
指定路徑的方式
update_in(struct.foo.bar, &(&1 + 1))
請注意,為了讓這個巨集運作,這個巨集必須始終可見完整的路徑。如需有關支援路徑表達式的更多資訊,請查看 get_and_update_in/2
文件。
範例
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> update_in(users["john"][:age], &(&1 + 1))
%{"john" => %{age: 28}, "meg" => %{age: 23}}
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> update_in(users["john"].age, &(&1 + 1))
%{"john" => %{age: 28}, "meg" => %{age: 23}}
更新巢狀結構中的鍵。
使用 Access
模組根據指定的 keys
遍歷結構,除非 key
是函式。如果金鑰是函式,它會如 get_and_update_in/3
中所指定的方式被呼叫。
data
是巢狀結構(也就是實作 Access
行為的地圖、關鍵字清單或結構)。fun
參數接收 key
的值(或 nil
,如果 key
不存在),而結果取代結構中的值。
範例
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> update_in(users, ["john", :age], &(&1 + 1))
%{"john" => %{age: 28}, "meg" => %{age: 23}}
請注意,提供給匿名函式的目前值可能是 nil
。如果任何中間值為 nil,則會引發
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> update_in(users, ["jane", :age], & &1 + 1)
** (ArgumentError) could not put/update key :age on a nil value
在目前內容中使用指定的模組。
呼叫時
use MyModule, some: :options
Elixir 會呼叫 MyModule.__using__/1
,傳遞 use
的第二個參數作為參數。由於 __using__/1
通常是巨集,所有一般的巨集規則都適用,而其回傳值應該是引號包住的程式碼,然後插入呼叫 use/2
的地方。
程式碼注入
use MyModule
在呼叫者中作為程式碼注入點。由於呼叫use MyModule
的呼叫者幾乎無法控制程式碼的注入方式,因此use/2
應該謹慎使用。如果可以,請盡量避免使用,而改用import/2
或alias/2
。
範例
例如,要使用 Elixir 提供的 ExUnit
架構撰寫測試案例,開發人員應該 use
ExUnit.Case
模組
defmodule AssertionTest do
use ExUnit.Case, async: true
test "always pass" do
assert true
end
end
在此範例中,Elixir 會呼叫 ExUnit.Case
模組中的 __using__/1
巨集,傳遞關鍵字清單 [async: true]
作為參數。
換句話說,use/2
轉換為
defmodule AssertionTest do
require ExUnit.Case
ExUnit.Case.__using__(async: true)
test "always pass" do
assert true
end
end
其中 ExUnit.Case
定義 __using__/1
巨集
defmodule ExUnit.Case do
defmacro __using__(opts) do
# do something with opts
quote do
# return some code to inject in the caller
end
end
end
最佳實務
__using__/1
通常用於需要設定某些狀態(透過模組屬性)或呼叫(例如 @before_compile
,請參閱 Module
的文件以取得更多資訊)到呼叫者時。
__using__/1
也可用於別名、需要或匯入不同模組的功能
defmodule MyModule do
defmacro __using__(_opts) do
quote do
import MyModule.Foo
import MyModule.Bar
import MyModule.Baz
alias MyModule.Repo
end
end
end
但是,如果 __using__/1
僅用於匯入、別名或需要模組本身,請勿提供。例如,避免這樣做
defmodule MyModule do
defmacro __using__(_opts) do
quote do
import MyModule
end
end
end
在這種情況下,開發人員應該直接匯入或別名模組,以便他們可以依自己的喜好自訂,而不用透過 use/2
的間接方式。開發人員也必須避免在 __using__/1
內定義函式。
假設 use MyModule
可以產生任何程式碼,開發人員可能很難了解 use MyModule
的影響。
因此,為了提供指導和清晰度,我們建議開發人員在 @moduledoc
中包含一個勸告區塊,說明 use MyModule
如何影響他們的程式碼。例如,GenServer
文件中概述
use GenServer
當您
use GenServer
時,GenServer
模組將設定@behaviour GenServer
並定義child_spec/1
函式,因此您的模組可以用作監督樹狀結構中的子項。
這提供了使用模組如何影響使用者程式碼的快速摘要。請記住,只列出對模組的公開 API 所做的變更。例如,如果 use MyModule
設定一個稱為 @_my_module_info
的內部屬性,而這個屬性從未打算公開,則不應列出。
為方便起見,產生上述勸告區塊的標記符號表示法為
> #### `use GenServer` {: .info}
>
> When you `use GenServer`, the GenServer module will
> set `@behaviour GenServer` and define a `child_spec/1`
> function, so your module can be used as a child
> in a supervision tree.
標記指定的變數不應衛生化。
這個巨集需要一個變數,它通常在 quote/2
內部呼叫,以標示不應對變數進行衛生處理。請參閱 quote/2
以取得更多資訊。
範例
iex> Kernel.var!(example) = 1
1
iex> Kernel.var!(example)
1
管道運算子。
這個運算子將左側的表達式作為右側函式呼叫的第一個引數。
範例
iex> [1, [2], 3] |> List.flatten()
[1, 2, 3]
上面的範例與呼叫 List.flatten([1, [2], 3])
相同。
|>/2
運算子在希望執行一系列類似管線的運算時最為有用
iex> [1, [2], 3] |> List.flatten() |> Enum.map(fn x -> x * 2 end)
[2, 4, 6]
在上面的範例中,清單 [1, [2], 3]
傳遞為 List.flatten/1
函式的第一個引數,然後將扁平化的清單傳遞為 Enum.map/2
函式的第一個引數,而 Enum.map/2
函式會將清單中的每個元素加倍。
換句話說,上面的表達式僅轉換為
Enum.map(List.flatten([1, [2], 3]), fn x -> x * 2 end)
陷阱
使用管道運算子時有兩個常見的陷阱。
第一個與運算子優先順序有關。例如,以下表達式
String.graphemes "Hello" |> Enum.reverse
轉換為
String.graphemes("Hello" |> Enum.reverse())
導致錯誤,因為 Enumerable
協定未定義為二進位檔案。加入明確的括號可以解決歧義
String.graphemes("Hello") |> Enum.reverse()
或者,更好的是
"Hello" |> String.graphemes() |> Enum.reverse()
第二個限制是 Elixir 總是傳遞到函式呼叫。因此,要傳遞到匿名函式,您需要呼叫它
some_fun = &Regex.replace(~r/l/, &1, "L")
"Hello" |> some_fun.()
或者,您可以使用 then/2
來達到相同的效果
some_fun = &Regex.replace(~r/l/, &1, "L")
"Hello" |> then(some_fun)
then/2
最常使用在您想要傳遞到函式,但值預期在第一個參數之外時,例如上面。透過將 some_fun
替換為其值,我們得到
"Hello" |> then(&Regex.replace(~r/l/, &1, "L"))
布林「或」運算子。
提供一個短路運算子,僅在第一個表達式未評估為真值 (也就是說,它是 nil
或 false
) 時才評估並傳回第二個表達式。否則傳回第一個表達式。
不允許在防護子句中使用。
範例
iex> Enum.empty?([1]) || Enum.empty?([1])
false
iex> List.first([]) || true
true
iex> Enum.empty?([1]) || 1
1
iex> Enum.empty?([]) || throw(:bad)
true
請注意,與 or/2
不同,此運算子接受任何表達式作為第一個參數,而不仅仅是布林值。