Y 分鐘學 Elixir

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"

操做符(Operators)

一些數學運算

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 也提供了 ||, &&! 能夠接受任意的類型。falsenil 其它都會被看成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 :「實際的順序並不重要, 可是,一個總體排序是否經明確界定是很是重要的。」

控制結構(Control Flow)

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"

模塊和函數(Modules and Functions)

匿名函數 (注意點)

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)

函數定義一樣支持 guards 和 多重子句:

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

Elixir 模塊支持屬性,模塊內建了一些屬性,你也能夠自定義屬性

defmodule MyMod do
  @moduledoc """
  內置的屬性,模塊文檔
  """

  @my_data 100 # 自定義屬性
  IO.inspect(@my_data) #=> 100
end

記錄和異常(Records and Exceptions)

記錄就是把特定值關聯到某個名字的結構體

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

併發(Concurrency)

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

相關文章
相關標籤/搜索