13-別名和程式碼引用
13-別名和程式碼引用
別名
require
import
別名機制
巢狀
為了實現軟體重用,Elixir提供了三種指令(directives)。之所以稱之為“指令”是因為它們的作用域是詞法作用域(lexical scope)。
13.1-別名
巨集alias
可以為任何模組名設定別名。想象一下Math模組,它針對特殊的數學運算使用了特殊的列表實現:
defmodule Math do
alias Math.List, as: List
end
現在,任何對List
的引用將被自動變成對Math.List
的引用。
如果還想訪問原來的List
,需要字首'Elixir':
List.flatten #=> uses Math.List.flatten
Elixir.List.flatten #=> uses List.flatten
Elixir.Math.List.flatten #=> uses Math.List.flatten
Elixir中定義的所有模組都在一個主Elixir名稱空間。但是為方便起見,我們平時都不再前面加‘Elixir’。
別名常被使用與定義快捷方式中。實際上不帶as
選項去呼叫alias
會自動將這個別名設定為模組名的最後一部分:
alias Math.List
就相當於:
alias Math.List, as: List
注意,別名是詞法作用域。即,允許你在某個函式中設定別名:
defmodule Math do
def plus(a, b) do
alias Math.List
# ...
end
def minus(a, b) do
# ...
end
end
上面例子中,alias
指令只在函式plus/2
中有用,minus/2
不受影響。
13.2-require
Elixir提供了許多巨集,用於超程式設計(寫能生成程式碼的程式碼)。
巨集就是一堆程式碼,只是它是在編譯時被展開和執行。就是說,為了使用一個巨集,你需要確保它的模組和實現在編譯時可用。
這使用require
指令:
iex> Integer.odd?(3)
** (CompileError) iex:1: you must require Integer before invoking the macro Integer.odd?/1
iex> require Integer
nil
iex> Integer.odd?(3)
true
Elixir中,Integer.odd?/1
函式被定義為一個巨集,因此他可以被當作衛兵表示式(guards)使用。
為了呼叫這個巨集,你首先得確保用require
引用了Integer模組。
總的來說,一個模組在被用到之前不需要被require引用,除非我們想讓這個巨集在整個模組中可用。
嘗試呼叫一個沒有引入的巨集會導致報錯。注意,像alias
指令一樣,require
也是詞法作用域的。
下文中我們會進一步討論巨集。
13.3-import
當我們想輕鬆地訪問別的模組中的函式和巨集時,我們使用import
指令加上那個模組完整的名字。
例如,如果我們想多次使用List模組中的duplicate
函式,我們可以簡單地import它:
iex> import List, only: [duplicate: 2]
nil
iex> duplicate :ok, 3
[:ok, :ok, :ok]
這個例子中,我們只從List模組匯入了函式duplicate/2
。儘管only:
選項是可選的,但是推薦使用。
除了only:
選項,還有except:
選項。
使用選項only:
還可以傳遞給它:macros
或:functions
。如下面例子,程式僅匯入Integer模組中所有的巨集:
import Integer, only: :macros
或者,僅匯入所有的函式:
import Integer, only: :functions
注意,import
也是詞法作用域,意味著我們可以在某特定函式中匯入巨集或方法:
defmodule Math do
def some_function do
import List, only: [duplicate: 2]
# call duplicate
end
end
在此例子中,匯入的函式List.duplicate/2
只在那個函式中可見。這個模組中其它函式都不可用(自然,別的模組也不受影響)。
注意,若import一個模組,將自動require它。
13.4-別名機制
講到這裡你會問,Elixir的別名到底是什麼,它是怎麼實現的?
Elixir中的別名是以大寫字母開頭的識別符號(像String, Keyword等等),在編譯時會被轉換為原子。
例如,別名‘String’會被轉換為:"Elixir.String"
:
iex> is_atom(String)
true
iex> to_string(String)
"Elixir.String"
iex> :"Elixir.String"
String
使用alias/2
指令,其實只是簡單地改變了這個別名將要轉換的結果。
別名如此工作,是因為在Erlang虛擬機器中(以及後來的Elixir),模組是被表述成原子的。例如,我們呼叫一個Erlang模組的機制是:
iex> :lists.flatten([1,[2],3])
[1, 2, 3]
這也是允許我們動態呼叫某模組內給定函式的機制:
iex> mod = :lists
:lists
iex> mod.flatten([1,[2],3])
[1,2,3]
一句話,我們只是簡單地在原子:lists
上呼叫了函式flatten
。
13.5-巢狀
介紹了別名,現在可以講講巢狀(nesting)以及它在Elixir中是如何工作的。
考慮下面的例子:
defmodule Foo do
defmodule Bar do
end
end
該例子定義了兩個模組Foo和Foo.Bar。後者在Foo中可以用Bar為名來訪問,因為它們在同一個詞法作用域中。 如果之後開發者決定把Bar模組挪到另一個檔案中,那它就需要以全名(Foo.Bar)或者別名來指代。
換句話說,上面的程式碼等同於:
defmodule Elixir.Foo do
defmodule Elixir.Foo.Bar do
end
alias Elixir.Foo.Bar, as: Bar
end
在以後章節我們可以看到,別名在巨集機制中扮演了很重要的角色,來保證巨集是乾淨的(hygienic)。
討論到這裡,模組基本上講得差不多了。之後會講解模組的屬性。
相關文章
- thawte程式碼簽名證書和Comodo程式碼簽名證書區別
- C#程式設計引用型別和值型別 以及引用傳遞和值傳遞C#程式設計型別
- 值型別和引用型別型別
- javascript引用型別資料使用程式碼例項JavaScript型別
- Java引用型別解析:掌握強引用、軟引用、弱引用和幻象引用的妙用Java型別
- JavaScript值型別和引用型別JavaScript型別
- Swift值型別和引用型別Swift型別
- 13-網路程式設計程式設計
- 未能找到型別或名稱空間名稱“MySql”(是否缺少 using 指令或程式集引用?)型別MySql
- 防止別人通過框架引用本站頁面程式碼框架
- js基本型別和引用型別區別JS型別
- 詳解C++中指標(*)、取地址(&)、解引用(*)與引用(&)的區別 (完整程式碼)C++指標
- Java的基本型別和引用型別Java型別
- require和import引用的區別UIImport
- 氣泡排序和引用型別排序型別
- 指標和引用的區別指標
- 007 Rust死靈書筆記之引用與別名Rust筆記
- JS篇-基本型別和引用型別、typeofJS型別
- EV程式碼簽名證書和標準程式碼簽名證書有何不同?
- 低程式碼和無程式碼的區別
- c#中值型別和引用型別的區別C#型別
- ElasticSearch模板和別名Elasticsearch
- javascript引用型別資料特點簡單程式碼例項JavaScript型別
- javascript原始值和引用值型別及區別JavaScript型別
- 普通OV版程式碼簽名證書,與EV程式碼簽名證書的作用以及區別
- 區別值型別資料和引用型別資料型別
- 無程式碼和低程式碼有哪些區別
- 程式碼簽名證書與SSL證書區別
- webpack的alias別名引用 在webstorm中點選不能跳轉?WebORM
- 別名的使用和nullNull
- c++中指標和引用的區別?C++指標
- C++中指標和引用的區別C++指標
- 引用型別型別
- 從賦值看基本型別和引用型別的區別賦值型別
- Python引用型別和值型別的區別與使用Python型別
- 強引用、軟引用、弱引用、幻象引用有什麼區別?
- C/C++引用和指標的聯絡和區別C++指標
- JAVA中基本資料型別和引用資料型別Java資料型別