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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM