1. 程式人生 > >Erlang 和 Elixir的差異

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中, 原子是以任意小寫字母開頭的識別符號號. 例如oktupledonut. 大寫字母開頭的任意標識被作為變數名稱.

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

區別:

  1. cond允許左側為任意表達式, erlang只允許guard子句.

  2. cond中的條件只有在表示式為nilfalse的時候為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