8-模組
8-模組
編譯
指令碼模式
命名函式
函式捕捉
預設引數
Elixir中我們把許多函式組織成一個模組。我們在前幾章已經提到了許多模組,如String模組:
iex> String.length "hello"
5
建立自己的模組,用defmodule
巨集。用def
巨集在其中定義函式:
iex> defmodule Math do
...> def sum(a, b) do
...> a + b
...> end
...> end
iex> Math.sum(1, 2)
3
像ruby一樣,模組名大寫起頭
8.1-編譯
通常把模組寫進檔案,這樣可以編譯和重用。假如檔案math.ex
有如下內容:
defmodule Math do
def sum(a, b) do
a + b
end
end
這個檔案可以用elixirc
進行編譯:
$ elixirc math.ex
這將生成名為Elixir.Math.beam
的位元組碼檔案。
如果這時再啟動iex,那麼這個模組就已經可以用了(假如在含有該編譯檔案的目錄啟動iex):
iex> Math.sum(1, 2)
3
Elixir工程通常組織在三個資料夾裡:
- ebin,放置編譯後的位元組碼(.beam檔案)
- lib,放置Elixir原始碼(.ex檔案)
- test,測試程式碼(.exs指令碼檔案)
實際專案中,構建工具Mix會負責編譯,並且設定好正確的路徑。 而為了學習方便,Elixir也提供了指令碼模式,可以更靈活而不用編譯。
8.2-指令碼模式
除了.ex檔案,Elixir還支援.exs指令碼檔案。Elixir對兩種檔案一視同仁,唯一區別是.ex檔案有待編譯, 而.exs檔案用來作指令碼執行,不需要編譯。例如,如下建立名為math.exs的檔案:
defmodule Math do
def sum(a, b) do
a + b
end
end
IO.puts Math.sum(1, 2)
執行之:
$ elixir math.exs
檔案將在記憶體中編譯和執行,列印出“3”作為結果。沒有位元組碼檔案生成。
後文中(為了學習和練習方便),推薦使用指令碼模式執行學到的程式碼。
8.3-命名函式
在某模組中,我們可以用def/2
巨集定義函式,用defp/2
定義私有函式。
用def/2
定義的函式可以被其它模組中的程式碼使用,而私有函式僅在定義它的模組內使用。
defmodule Math do
def sum(a, b) do
do_sum(a, b)
end
defp do_sum(a, b) do
a + b
end
end
Math.sum(1, 2) #=> 3
Math.do_sum(1, 2) #=> ** (UndefinedFunctionError)
函式宣告也支援使用衛兵或多個子句。 如果一個函式有好多子句,Elixir會匹配每一個子句直到找到一個匹配的。 下面例子檢查引數是否是數字:
defmodule Math do
def zero?(0) do
true
end
def zero?(x) when is_number(x) do
false
end
end
Math.zero?(0) #=> true
Math.zero?(1) #=> false
Math.zero?([1,2,3])
#=> ** (FunctionClauseError)
如果沒有一個子句能匹配引數,會報錯。
8.4-函式捕捉
本教程中提到函式,都是用name/arity
的形式描述。這種表示方法可以被用來獲取一個命名函式(賦給一個函式型變數)。
下面用iex執行一下前面章節定義的math.exs檔案:
$ iex math.exs
執行後,該檔案中定義的模組也就被匯入當前上下文了。
iex> Math.zero?(0)
true
iex> fun = &Math.zero?/1
&Math.zero?/1
iex> is_function fun
true
iex> fun.(0)
true
用&<function notation>
從函式名捕捉一個函式,它本身代表該函式值(函式型別的值)。
它可以不必賦給一個變數,直接用括號來使用該函式。
當前上下文定義的,或者已匯入的函式,比如is_function/1
,可以不字首模組名:
iex> &is_function/1
&:erlang.is_function/1
iex> (&is_function/1).(fun)
true
這種語法還可以作為快捷方式使用來建立函式:
iex> fun = &(&1 + 1)
#Function<6.71889879/1 in :erl_eval.expr/5>
iex> fun.(1)
2
&1
表示傳給該函式的第一個引數。上面例子中,&(&1+1)
其實等同於fn x->x+1 end
。
在建立短小函式時,這種寫法很方便。
想要了解更多關於&
捕捉操作符,參考Kernel.SpecialForms文件。
8.5-預設引數
Elixir中,命名函式也支援預設引數:
defmodule Concat do
def join(a, b, sep \\ " ") do
a <> sep <> b
end
end
IO.puts Concat.join("Hello", "world") #=> Hello world
IO.puts Concat.join("Hello", "world", "_") #=> Hello_world
任何表示式都可以作為預設引數,但是隻在函式呼叫時用到了才被執行(函式定義時,那些表示式只是存在那兒,不執行;函式呼叫時,沒有用到預設值,也不執行)。
defmodule DefaultTest do
def dowork(x \\ IO.puts "hello") do
x
end
end
然後執行:
iex> DefaultTest.dowork 123
123
iex> DefaultTest.dowork
hello
:ok
如果有預設引數值的函式有了多條子句,推薦先定義一個函式頭(無具體函式體)僅為了宣告這些預設值:
defmodule Concat do
def join(a, b \\ nil, sep \\ " ")
def join(a, b, _sep) when nil?(b) do
a
end
def join(a, b, sep) do
a <> sep <> b
end
end
IO.puts Concat.join("Hello", "world") #=> Hello world
IO.puts Concat.join("Hello", "world", "_") #=> Hello_world
IO.puts Concat.join("Hello") #=> Hello
使用預設值時,注意對函式過載會有一定影響。考慮下面例子:
defmodule Concat do
def join(a, b) do
IO.puts "***First join"
a <> b
end
def join(a, b, sep \\ " ") do
IO.puts "***Second join"
a <> sep <> b
end
end
如果將以上程式碼儲存在檔案“concat.ex”中並編譯,Elixir會報出以下警告:
concat.ex:7: this clause cannot match because a previous clause at line 2 always matches
編譯器是在警告我們,在使用兩個引數呼叫join
函式時,總使用第一個函式定義。
只有使用三個引數呼叫時,才會使用第二個定義:
$ iex concat.exs
.
iex> Concat.join "Hello", "world"
***First join
"Helloworld"
iex> Concat.join "Hello", "world", "_"
***Second join
"Hello_world"
後面幾章將介紹使用命名函式來做迴圈,如何從別的模組中匯入函式,以及模組的屬性等。
相關文章
- 8-集合
- 序列化模組,隨機數模組,os模組,sys模組,hashlib模組隨機
- python 模組:itsdangerous 模組Python
- path模組 fs模組
- Python模組:time模組Python
- day18:json模組&time模組&zipfile模組JSON
- Python模組之urllib模組Python
- python模組之collections模組Python
- CommonJS模組 和 ECMAScript模組JS
- 序列化模組,subprocess模組,re模組,常用正則
- 聊天模組及分享模組分享
- [Python模組學習] glob模組Python
- 模組學習之hashlib模組
- 模組學習之logging模組
- Python常用模組(random隨機模組&json序列化模組)Pythonrandom隨機JSON
- 模組
- ECDSA—模乘模組
- rust-algorithms:8-堆排序RustGo排序
- Python入門(二十六):檔案模組(os模組與shutil模組)Python
- Python模組、第三方模組安裝、模組匯入教程Python
- Profinet遠端IO模組:模擬量模組_軟體組態說明
- time模組,collections模組,佇列和棧佇列
- python–模組之random隨機數模組Pythonrandom隨機
- python–模組之os操作檔案模組Python
- Vue — 請求模組、api模組封裝VueAPI封裝
- 【StoneDB 模組介紹】伺服器模組伺服器
- Python基礎12(模組與datetime模組)Python
- collections模組
- json模組JSON
- JAVA - 模組Java
- Ansible——模組
- pymysql模組MySql
- js 模組JS
- re模組
- JavaScript 模組JavaScript
- ansible模組
- requests模組
- 配置模組
- Python 模組Python