Elixir 是一門構建在Erlang VM 之上的函數式編程語言。Elixir 徹底兼容 Erlang, 另外還提供了更標準的語法和特性。html
# 這是單行註釋, 註釋以井號開頭 # 沒有多行註釋 # 但你能夠堆疊多個註釋。
elixir shell 使用命令 iex
進入。編譯模塊使用 elixirc
命令。若是安裝正確,這些命令都會在環境變量裏web
數字shell
3 # 整型 0x1F # 整型 3.0 # 浮點類型
原子(Atoms) 以 :
開頭編程
:hello # atom
元組(Tuple) 在內存中的存儲是連續的segmentfault
{1,2,3} # tuple
使用elem
函數訪問元組(tuple)裏的元素:併發
elem({1, 2, 3}, 0) #=> 1
列表(list)less
[1,2,3] # list
能夠用下面的方法訪問列表的頭尾元素:編程語言
[head | tail] = [1,2,3] head #=> 1 tail #=> [2,3]
在elixir,就像在Erlang, =
表示模式匹配 (pattern matching) ,不是賦值。這表示會用左邊的模式(pattern)匹配右側。上面的例子中訪問列表的頭部和尾部就是這樣工做的。ide
當左右兩邊不匹配時,會返回error, 在這個例子中,元組大小不同。函數式編程
# {a, b, c} = {1, 2} #=> ** (MatchError) no match of right hand side value: {1,2}
還有二進制類型 (binaries)
<<1,2,3>> # binary
字符串(Strings) 和 字符列表(char lists)
"hello" # string 'hello' # char list
多行字符串
""" I'm a multi-line string. """ "I'm a multi-line\nstring.\n"
全部的字符串(Strings)以UTF-8編碼:
"héllò" #=> "héllò"
字符串(Strings)本質就是二進制類型(binaries), 字符列表(char lists)本質是列表(lists)
<<?a, ?b, ?c>> #=> "abc" [?a, ?b, ?c] #=> 'abc'
在 elixir中,?a
返回 a
的 ASCII 整型值
?a #=> 97
++
, 對於二進制類型則使用 <>
[1,2,3] ++ [4,5] #=> [1,2,3,4,5] 'hello ' ++ 'world' #=> 'hello world' <<1,2,3>> <> <<4,5>> #=> <<1,2,3,4,5>> "hello " <> "world" #=> "hello world"
一些數學運算
1 + 1 #=> 2 10 - 5 #=> 5 5 * 2 #=> 10 10 / 2 #=> 5.0
在 elixir 中,操做符 /
返回值老是浮點數。
作整數除法使用 div
div(10, 2) #=> 5
爲了獲得餘數使用 rem
rem(10, 3) #=> 1
還有 boolean 操做符: or
, and
and not
。第一個參數必須是boolean 類型。
true and true #=> true false or true #=> true # 1 and true #=> ** (ArgumentError) argument error
Elixir 也提供了 ||
, &&
和 !
能夠接受任意的類型。false
和 nil
其它都會被看成true。
1 || true #=> 1 false && 1 #=> false nil && 20 #=> nil !true #=> false
比較有: ==
, !=
, ===
, !==
, <=
, >=
, <
和 >
1 == 1 #=> true 1 != 1 #=> false 1 < 2 #=> true
===
和 !==
在比較整型和浮點類型時更爲嚴格:
1 == 1.0 #=> true 1 === 1.0 #=> false
1 < :hello #=> true
總的排序順序定義以下:
number < atom < reference < functions < port < pid < tuple < list < bit string
引用Joe Armstrong :「實際的順序並不重要, 可是,一個總體排序是否經明確界定是很是重要的。」
if
表達式
if false do "This will never be seen" else "This will" end
還有 unless
unless true do "This will never be seen" else "This will" end
在Elixir中,不少控制結構都依賴於模式匹配
case
容許咱們把一個值與多種模式進行比較:
case {:one, :two} do {:four, :five} -> "This won't match" {:one, x} -> "This will match and assign `x` to `:two`" _ -> "This will match any value" end
模式匹配時,若是不須要某個值,通用的作法是把值 匹配到 _
例如,咱們只須要要列表的頭元素:
[head | _] = [1,2,3] head #=> 1
下面的方式效果同樣,但可讀性更好
[head | _tail] = [:a, :b, :c] head #=> :a
cond
能夠檢測多種不一樣的分支。使用 cond
代替多個if
表達式嵌套。
cond do 1 + 1 == 3 -> "I will never be seen" 2 * 5 == 12 -> "Me neither" 1 + 2 == 3 -> "But I will" end
常常能夠看到最後一個條件等於'true',這將老是匹配。
cond do 1 + 1 == 3 -> "I will never be seen" 2 * 5 == 12 -> "Me neither" true -> "But I will (this is essentially an else)" end
try/catch
用於捕獲被拋出的值, 它也支持 after
子句,不管是否值被捕獲,after 子句都會被調用。
try/catch
try do throw(:hello) catch message -> "Got #{message}." after IO.puts("I'm the after clause.") end #=> I'm the after clause # "Got :hello"
匿名函數 (注意點)
square = fn(x) -> x * x end square.(5) #=> 25
也支持接收多個子句和衛士(guards)。
Guards 能夠進行模式匹配。
Guards 使用when
關鍵字指明:
f = fn x, y when x > 0 -> x + y x, y -> x * y end f.(1, 3) #=> 4 f.(-1, 3) #=> -3
Elixir 提供了不少內建函數,在默認做用域都是可用的。
is_number(10) #=> true is_list("hello") #=> false elem({1,2,3}, 0) #=> 1
你能夠在一個模塊裏定義多個函數,定義函數使用 def
defmodule Math do def sum(a, b) do a + b end def square(x) do x * x end end Math.sum(1, 2) #=> 3 Math.square(3) #=> 9
保存到 math.ex
,使用 elixirc
編譯你的 Math 模塊。
在終端裏:
elixirc math.ex
在模塊中可使用def
定義函數,使用 defp
定義私有函數。 使用def
定義的函數能夠被其它模塊調用, 私有函數只能在本模塊內調用。
defmodule PrivateMath do def sum(a, b) do do_sum(a, b) end defp do_sum(a, b) do a + b end end PrivateMath.sum(1, 2) #=> 3 # PrivateMath.do_sum(1, 2) #=> ** (UndefinedFunctionError)
defmodule Geometry do def area({:rectangle, w, h}) do w * h end def area({:circle, r}) when is_number(r) do 3.14 * r * r end end Geometry.area({:rectangle, 2, 3}) #=> 6 Geometry.area({:circle, 3}) #=> 28.25999999999999801048 # Geometry.area({:circle, "not_a_number"}) #=> ** (FunctionClauseError) no function clause matching in Geometry.area/1
因爲不變性,遞歸是Elixir的重要組成部分
defmodule Recursion do def sum_list([head | tail], acc) do sum_list(tail, acc + head) end def sum_list([], acc) do acc end end Recursion.sum_list([1,2,3], 0) #=> 6
defmodule MyMod do @moduledoc """ 內置的屬性,模塊文檔 """ @my_data 100 # 自定義屬性 IO.inspect(@my_data) #=> 100 end
defrecord Person, name: nil, age: 0, height: 0 joe_info = Person.new(name: "Joe", age: 30, height: 180) #=> Person[name: "Joe", age: 30, height: 180]
訪問name的值
joe_info.name #=> "Joe"
更新age的值
joe_info = joe_info.age(31) #=> Person[name: "Joe", age: 31, height: 180]
使用 try
rescue
進行異常處理
try do raise "some error" rescue RuntimeError -> "rescued a runtime error" _error -> "this will rescue any error" end
全部的異常都有一個message
try do raise "some error" rescue x in [RuntimeError] -> x.message end
Elixir 依賴於actor併發模型。在Elixir編寫併發程序的三要素:
啓動一個新的進程使用spawn
函數,接收一個函數做爲參數
f = fn -> 2 * 2 end #=> #Function<erl_eval.20.80484245> spawn(f) #=> #PID<0.40.0>
spawn 函數返回一個pid(進程標識符),你可使用pid向進程發送消息。使用 <-
操做符發送消息。咱們須要在進程內接收消息,要用到 receive
機制。
defmodule Geometry do def area_loop do receive do {:rectangle, w, h} -> IO.puts("Area = #{w * h}") area_loop() {:circle, r} -> IO.puts("Area = #{3.14 * r * r}") area_loop() end end end
編譯這個模塊,在shell中建立一個進程,並執行 area_looop
函數。
pid = spawn(fn -> Geometry.area_loop() end) #=> #PID<0.40.0>
發送一個消息給 pid
, 會在receive語句進行模式匹配
pid <- {:rectangle, 2, 3} #=> Area = 6 # {:rectangle,2,3} pid <- {:circle, 2} #=> Area = 12.56000000000000049738 # {:circle,2}
shell也是一個進程(process), 你可使用self
獲取當前 pid
self() #=> #PID<0.27.0>
via learnxinyminutes