#引用與去引用api
一個Elixir程序能夠用它本身的數據結構來表現.本章,咱們將會學習這些結構體的特色和如何組成它們.本章咱們要學習的概念是爲宏的積木(building blocks),在下一章中咱們將深刻研究它.數據結構
#引用(Quoting)函數
Elixir程序中的積木是一個三元素元組.例如,函數sum(1, 2, 3)
的內部表示是:學習
{:sum, [], [1, 2, 3]}
你能夠用quote
宏來獲得任何表達式的內部表現:ui
iex> quote do: sum(1, 2, 3) {:sum, [], [1, 2, 3]}
第一個元素是函數名,第二個元素是一個包含了元數據的關鍵詞列表,第三個元素是參數列表.this
操做符也能夠用元組來表示:atom
iex> quote do: 1 + 2 {:+, [context: Elixir, import: Kernel], [1, 2]}
甚至一個映射都被表示成對%{}
的調用:code
iex> quote do: %{1 => 2} {:%{}, [], [{1, 2}]}
變量也能這樣三段式表示,只不過最後的元素換成了一個原子:orm
iex> quote do: x {:x, [], Elixir}
當引用更復雜的表達式時,代碼被表示成了一個樹狀的嵌套元組.許多語言將這種表示稱爲抽象語法樹(AST).ELixir稱其爲引用表達式:教程
iex> quote do: sum(1, 2 + 3, 4) {:sum, [], [1, {:+, [context: Elixir, import: Kernel], [2, 3]}, 4]}
有時,把引用表達式還原爲文本代碼會頗有用.能夠用Macro.to_string/1
來完成:
iex> Macro.to_string(quote do: sum(1, 2 + 3, 4)) "sum(1, 2 + 3, 4)"
一般,上述元組的結構會是這種格式:
{atom | tuple, list, list | atom}
- 第一個元素是一個原子,或者是一樣表達方式的另外一個元組; - 第二個元素是一個關鍵詞列表,包含元數據,好比數字和語境; - 第三個元素是函數調用的參數列表或者是一個原子.當爲原子時,意味着元組表示的是一個變量.
除了上面定義的元組,有五個Elixir字面量,在被引用時,會返回它們本身(而不是一個元組).它們是:
:sum #=> Atoms 1.0 #=> Numbers [1, 2] #=> Lists "strings" #=> Strings {key, value} #=> Tuples with two elements
大多數Elixir代碼都有一個直截了當的引用表達式.咱們建議你嘗試不一樣的代碼,看看結果如何.好比,String.upcase("foo")
會如何展開?咱們已經知道if(true, do: :this, else: :that)
等同於if true do :this else :that end
.它要如何用引用表達式來容納?
#去引用(Unquoting)
引用能夠得到一些特定代碼塊的內部表達式.然而,有時咱們須要注入一些特定代碼塊到咱們想要獲取的表達式中.
例如,假設咱們有一個變量,包含了咱們想注入一個引用表達式中的數字,變量名爲number
.
iex> number = 13 iex> Macro.to_string(quote do: 11 + number) "11 + number"
那不是咱們想要的,number
變量被引入了表達式,但number
變量的值沒有被注入.爲了注入number
變量的值,咱們須要在引用表達式中使用unquote
:
iex> number = 13 iex> Macro.to_string(quote do: 11 + unquote(number)) "11 + 13"
unquote
甚至能夠被用於注入函數名:
iex> fun = :hello iex> Macro.to_string(quote do: unquote(fun)(:world)) "hello(:world)"
在相同的狀況下,可能須要注入一個有許多值的列表.好比,假設你有一個列表[1, 2, 6]
,咱們想把[3, 4, 5]
注入進去.使用unquote
卻不能獲得想要的結果:
iex> inner = [3, 4, 5] iex> Macro.to_string(quote do: [1, 2, unquote(inner), 6]) "[1, 2, [3, 4, 5], 6]"
這時就輪到unquote_splicing
出場了:
iex> inner = [3, 4, 5] iex> Macro.to_string(quote do: [1, 2, unquote_splicing(inner), 6]) "[1, 2, 3, 4, 5, 6]"
去引用在操做宏的時候頗有用.編寫宏的時候,開發者能夠獲取代碼塊並將它們注入到其它代碼塊中,這能夠被用於改變代碼或者編寫能在編譯時生成代碼的代碼.
#釋放(Escaping)
如本章開頭所見,Elixir中只有一部分值有合法的引用表達式.好比,一個映射沒有合法的引用表達式.四元素的元組也沒有.然而,這些值能夠被表示成一個引用表達式:
iex> quote do: %{1 => 2} {:%{}, [], [{1, 2}]}
有時你須要將這種值注入引用表達式中.咱們首先須要將這些值釋放到引用表達式中,使用Macro.escape/1
來完成:
iex> map = %{hello: :world} iex> Macro.escape(map) {:%{}, [], [hello: :world]}
宏接收引用表達式,並必須返回引用表達式.然而執行宏的過程當中,你可能須要處理一些值,而且區分值與引用表達式.
也就是說,區分常規的Elixir值(例如列表,映射,進程,參考等等)與引用表達式,是很重要的.有一些值的引用表達式就是它們本身,例如整數,原子覈字符串.另外一些值,好比映射,須要被顯式轉換.最後,函數與參考徹底不能被轉化成引用表達式.
你能夠在Kernel.SpecialForms
模塊中閱讀更多關於quote
和unquote
的內容.在Macro
模塊中能夠找到Macro.escape/1
的文檔和其它與引用表達式相關的函數.
在本教程中咱們將編寫本身的第一個宏,讓咱們進入下一章吧.