Erlang 和 Elixir的差異
原文: http://elixir-lang.org/crash-course.html
函式呼叫
Elixir允許你呼叫函式的時候省略括號, Erlang不行.
Erlang | Elixir |
---|---|
some_function(). |
some_function |
sum(A,B) |
sum a,b |
從模組中呼叫一個函式, 使用不同的語法, 在Erlang, 你可以寫:
lists:last([1,2]).
從List模組中呼叫last函式. 在Elixir中使用.
符號代替:
List.last([1,2])
注意. 因為Erlang模組以原子的形式標識, 你可以在Elixir中以如下方式呼叫Erlang函式.
:lists.sort [1,2,3]
所有Erlang內建函式存在於:erlang
模組中.
資料型別
原子
在Erlang中, 原子是以任意小寫字母開頭的識別符號號. 例如ok
, tuple
, donut
. 大寫字母開頭的任意標識被作為變數名稱.
Erlang
im_an_atom.
me_too.
Im_a_var.
X = 10.
Elixir
:im_an_atom
:me_too
im_a_var
x = 10
Module # 原子別名, 擴充套件為:`Elixir.Module`
還可以建立非小寫字母開頭的原子, 兩種語言的語法差異如下:
Erlang
is_atom(ok). %=> true
is_atom('0_ok'). %=> true is_atom('Multiple words'). %=> true is_atom(''). %=> true
Elixir
is_atom :ok #=> true
is_atom :'ok' #=> true is_atom Ok #=> true is_atom :"Multiple words" #=> true is_atom :"" #=> true
元組
元組的語法兩種語言相同, 但API有卻別.
列表和二進位制
Erlang
is_list('Hello'). %=> false
is_list("Hello"). %=> true is_binary(<<"Hello">>). %=> true
Elixir
is_list 'Hello' #=> true
is_binary "Hello" #=> true
is_binary <<"Hello">> #=> true <<"Hello">> === "Hello" #=> true
Elixir string
標識一個UTF-8編碼的二進位制, 同時有一個String
模組處理這類書籍.
Elixir假設你的原始檔是以UTF-8編碼的. Elixir中的string
是一個字元列表, 同時有一個:string
模組處理這類資料.
Elixir 支援多行字串(heredocs
):
is_binary """
This is a binary
spanning several
lines.
"""
#=> true
關鍵字列表
包含兩個元素的元組列表
Erlang
Proplist = [{another_key, 20}, {key, 10}].
proplists:get_value(another_key, Proplist).
%=> 20
Elixir
kw = [another_key: 20, key: 10]
kw[:another_key] #=> 20
Map
Erlang
Map = #{key => 0}. % 建立Map
Updated = Map#{key := 1}. % 更新Map中的值
#{key := Value} = Updated. % 匹配 Value =:= 1. %=> true
Elixir
map = %{:key => 0} # 建立Map
map = %{map | :key => 1} # 更新Map中的值 %{:key => value} = map # 匹配 value === 1 #=> true
注意:
1.key:
和 0
之間一定要有一個空格, 如下:
iex(2)> map = %{key:0}
**(SyntaxError) iex:2: keyword argument must be followed by space after: key: iex(2)> map = %{key: 0} %{key: 0}
2.所有的key必須是原子型別才能這麼用.
正則表示式
Elixir支援正則表示式的字面語法. 這樣的語法允許正則表示式在編譯時被編譯而不是執行時進行編譯, 並且不要求
轉義特殊的正則表示式符號:
Erlang
{ ok, Pattern } = re:compile("abc\\s").
re:run("abc ", Pattern).
%=> { match, ["abc "] }
Elixir:
Regex.run ~r/abc\s/, "abc "
#=> ["abc "]
正則表示式還能用在heredocs
當中, 提供了一個定義多行正則表示式的便捷的方法:
Regex.regex? ~r"""
This is a regex
spanning several
lines.
"""
模組
每個erlang模組儲存在其自己的檔案中, 並且有如下結構:
-module(hello_module).
-export([some_fun/0, some_fun/1]). % A "Hello world" function some_fun() -> io:format('~s~n', ['Hello world!']). % This one works only with lists some_fun(List) when is_list(List) -> io:format('~s~n', List). % Non-exported functions are private priv() -> secret_info.
這裡我們建立一個名為hello_module
的模組. 其中我們定義了三個函式, 前兩個用於其他模組呼叫, 並通過export
指令匯出. 它包含一個要匯出的函式列表, 其中沒個函式的書寫格式為<function name>/<arity>
. arity
標書引數的個數.
上面的Elixir等效程式碼為:
defmodule HelloModule do
# A "Hello world" function def some_fun do IO.puts "Hello world!" end # This one works only with lists def some_fun(list) when is_list(list) do IO.inspect list end # A private function defp priv do :secret_info end end
在Elixir中, 還可以在一個檔案中定義多個模組, 以及巢狀模組.
defmodule HelloModule do
defmodule Utils do def util do IO.puts "Utilize" end defp priv do :cant_touch_this end end def dummy do :ok end end defmodule ByeModule do end HelloModule.dummy #=> :ok HelloModule.Utils.util #=> "Utilize" HelloModule.Utils.priv #=> ** (UndefinedFunctionError) undefined function: HelloModule.Utils.priv/0
函式語法
Erlang 圖書的這章提供了模式匹配和函式語法的詳細描述. 這裡我簡單的覆蓋一些要點並提供Erlang和Elixir的程式碼示例:
模式匹配
Erlang
loop_through([H|T]) ->
io:format('~p~n', [H]),
loop_through(T);
loop_through([]) -> ok.
Elixir
def loop_through([h|t]) do
IO.inspect h
loop_through t
end def loop_through([]) do :ok end
當定義一個同名函式多次的時候, 沒個這樣的定義稱為分句
. 在Erlang中, 分句
總是緊挨著的, 並且由分號;
分割. 最後一個分句通過點.
終止.
Elixir並不要求使用標點符號來分割分句, 但是他們必須分組在一起(同名函式必須上下緊接著
)
標識函式
在Erlang和Elixir中, 函式不僅僅由名字來標識, 而是由名字
和引數的個數
共同來標識一個函式. 在下面兩個例子中, 我們定義了四個不同的函式(全部名為sum
, 不同的引數個數)
Erlang
sum() -> 0.
sum(A) -> A. sum(A, B) -> A + B. sum(A, B, C) -> A + B + C.
Elixir
def sum, do: 0 def sum(a), do: a def sum(a, b), do: a + b def sum(a, b, c), do: a + b + c
基於某些條件, Guard表示式(Guard expressions), 提供了一個精確的方式定義接受特定取值的函式
Erlang
sum(A, B) when is_integer(A), is_integer(B) -> A + B; sum(A, B) when is_list(A), is_list(B) -> A ++ B; sum(A, B) when is_binary(A), is_binary(B) -> <<A/binary, B/binary>>. sum(1, 2). %=> 3 sum([1], [2]). %=> [1,2] sum("a", "b"). %=> "ab"
Elixir
def sum(a, b) when is_integer(a) and is_integer(b) do a + b end def sum(a, b) when is_list(a) and is_list(b) do a ++ b end def sum(a, b) when is_binary(a) and is_binary(b) do a <> b end sum 1, 2 #=> 3 sum [1], [2] #=> [1,2] sum "a", "b" #=> "ab"
預設值
Erlang不支援預設值
Elixir 允許引數有預設值
def mul_by(x, n \\ 2) do x * n end mul_by 4, 3 #=> 12 mul_by 4 #=> 8
匿名函式
匿名函式以如下方式定義:
Erlang
Sum = fun(A, B) -> A + B end.
Sum(4, 3).
%=> 7 Square = fun(X) -> X * X end. lists:map(Square, [1, 2, 3, 4]). %=> [1, 4, 9, 16]
當定義匿名函式的時候也可以使用模式匹配.
Erlang
F = fun(Tuple = {a, b}) ->
io:format("All your ~p are belong to us~n", [Tuple]);
([]) ->
"Empty"
end.
F([]).
%=> "Empty" F({a, b}). %=> "All your {a,b} are belong to us"
Elixir
f = fn
{:a, :b} = tuple ->
IO.puts "All your #{inspect tuple} are belong to us" [] -> "Empty" end f.([]) #=> "Empty" f.({:a, :b}) #=> "All your {:a, :b} are belong to us"
一類函式
匿名函式是第一類值, 他們可以作為引數傳遞給其他函式, 也可以作為返回值. 這裡有一個特殊的語法允許命名函式以相同的方式對待:
Erlang
-module(math).
-export([square/1]). square(X) -> X * X. lists:map(fun math:square/1, [1, 2, 3]). %=> [1, 4, 9]
Elixir
defmodule Math do
def square(x) do x * x end end Enum.map [1, 2, 3], &Math.square/1 #=> [1, 4, 9]
Partials in Elixir
Elixir supports partial application of functions which can be used to define anonymous functions in a concise way:
Enum.map [1, 2, 3, 4], &(&1 * 2) #=> [2, 4, 6, 8] List.foldl [1, 2, 3, 4], 0, &(&1 + &2) #=> 10
Partials also allow us to pass named functions as arguments.
defmodule Math do
def square(x) do x * x end end Enum.map [1, 2, 3], &Math.square/1 #=> [1, 4, 9]
控制流
The constructs if
and case
are actually expressions in both Erlang and Elixir, but may be used for control flow as in imperative languages.
Case
The case construct provides control flow based purely on pattern matching.
Erlang
case {X, Y} of
{a, b} -> ok;
{b, c} -> good;
Else -> Else
end
Elixir
case {x, y} do
{:a, :b} -> :ok {:b, :c} -> :good other -> other end
If
Erlang
Test_fun = fun (X) ->
if X > 10 ->
greater_than_ten;
X < 10, X > 0 -> less_than_ten_positive; X < 0; X =:= 0 -> zero_or_negative; true -> exactly_ten end end. Test_fun(11). %=> greater_than_ten Test_fun(-2). %=> zero_or_negative Test_fun(10). %=> exactly_ten
Elixir
test_fun = fn(x) ->
cond do
x > 10 ->
:greater_than_ten x < 10 and x > 0 -> :less_than_ten_positive x < 0 or x === 0 -> :zero_or_negative true -> :exactly_ten end end test_fun.(44) #=> :greater_than_ten test_fun.(0) #=> :zero_or_negative test_fun.(10) #=> :exactly_ten
區別:
-
cond
允許左側為任意表達式, erlang只允許guard
子句. -
cond
中的條件只有在表示式為nil
和false
的時候為false, 其他情況一律為true, Erlang為一個嚴格的布林值
Elixr還提供了一個簡單的if
結構
if x > 10 do
:greater_than_ten
else
:not_greater_than_ten
end
傳送和接受訊息
Erlang
Pid = self().
Pid ! {hello}.
receive
{hello} -> ok;
Other -> Other
after
10 -> timeout
end.
Elixir
pid = Kernel.self
send pid, {:hello}
receive do
{:hello} -> :ok other -> other after 10 -> :timeout end
新增Elixir到現有的Erlang程式
Rebar整合
如果使用Rebar,可以把Elixir倉庫作為依賴.
https://github.com/elixir-lang/elixir.git
Elixir實際上是一個Erlang的app. 它被放在lib
目錄中, rebar不知道這樣的目錄結構. 因此需要制定Elixir的庫目錄.
{lib_dirs, [
"deps/elixir/lib"
]}.
轉自:https://segmentfault.com/a/1190000004882064