安裝 elixir
Installing Elixir 記錄了如何在不同 OS 安裝 elixir 的方法。
我是透過 macport 安裝 elixir
sudo port install elixir
elixir 是用 Erlang/OTP 20 開發的,所以要注意是不是已經有安裝了 erlang @20
$ port installed |grep erlang
erlang @20.1_0+hipe+ssl+wxwidgets (active)
CentOS 7 上安装 Elixir 文章提到可以從 elixir 的 github 下載 precompiled zip,解壓縮後就可以用了。
interactive shell: iex
iex 是可以直接 evaluate elixir expression 的互動 shell, h 是 help, i 可查看資料的資訊
$ iex
Erlang/OTP 20 [erts-9.0] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.5.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> h
iex(2)> h(v/0)
Returns the value of the nth expression in the history.
iex(3)> h(IO)
Functions handling input/output (IO).
iex(4)> > i 123
Term
123
Data type
Integer
Reference modules
Integer
Implemented protocols
IEx.Info, Inspect, List.Chars, String.Chars
離開 iex 的方式有兩種
- Ctrl-C 兩次
- Ctrl-G 然後輸入 q 及 Retuen
Customize iex: 在 IEx.configure 可查看 iex 所有可調整設定的選項
iex(1)> h IEx.configure
把設定的相關程式放在 ~/.iex.exs 就可以使用, ex:
IEx.configure(colors: [ eval_result: [:red, :bright] ])
IEx.configure(inspect: [limit: 10])
編譯與執行
hello.exs
IO.puts "Hello, World!"
$ elixir hello.exs
Hello, World!
或是在 iex 裡面執行
iex(1)> c "hello.exs"
Hello, World!
[]
Pattern Matching
elixir 將 = 視為 pattern matching 的 operator
iex(4)> list = [1, 2, [ 3, 4, 5 ] ]
[1, 2, [3, 4, 5]]
iex(5)> [a, b, c ] = list
[1, 2, [3, 4, 5]]
iex(6)> c
[3, 4, 5]
iex(7)> [a, 1, c ] = list
** (MatchError) no match of right hand side value: [1, 2, [3, 4, 5]]
_ 是 ignore 的意思,這跟 erlang 一樣
iex(7)> [a, _, c ] = list
[1, 2, [3, 4, 5]]
Variable 可在不同的 expression 重新被 assgin 新的 value,但如果要強制使用舊的 value,可使用 ^ pin operator
iex(1)> a=1
1
iex(2)> a=2
2
iex(3)> ^a=1
** (MatchError) no match of right hand side value: 1
Immutable Data
在 mutable data 的程式語言中,count 傳入 function 後,可能會在該 function 內改變數值,但 Immutable Data 強調 count 數值不變的特性。
count = 99
do_something_with(count)
print(count)
elixir 會複製一份 list1 的資料,加到 4 後面,然後指定給 list2
iex(3)> list1 = [ 3, 2, 1 ]
[3, 2, 1]
iex(4)> list2 = [ 4 | list1 ]
[4, 3, 2, 1]
用不到的變數,會自動以 Garbage Collection 的方式回收掉。因為 elixir 會有很多 processes,每個 process 都有自己的 heap,每個 heap 都是各自獨立的。
Elixir Data Types
Built-in Types
- Value types:
- Arbitrary-sized integers
- Floating-point numbers
- Atoms
- Ranges
- Regular expressions
- System types:
- PIDs and ports
- References
- Collection types:
- Tuples
- Lists
- Maps
- Binaries
- Functions
elixir 的檔案有兩種 extensions
- ex: for compiled code
- exs: for interpreted code
如果要寫 scripts 或是 test,要使用 .exs,其他狀況就用 .ex
Integers: 可寫成
- decimal: 123
- hexadecimal: 0xcafe
- octal: 0o777
binary: 0b10110
Floating-point numbers
- 1.0
- 0.2456
- 0.314159e1
314159.0e-5
- Atoms
erlang 的 atom 為小寫字母,變數為大寫,但在 elixir 是以 : 前置符號代表 atom
- :fred
- :is_binary?
- :var@2
- :<>
- :===
- :"func/3"
:"long john silver"
- Ranges: start..end
ex: 1..5
- Regular expressions: ~r{regexp} 或是 ~r{regexp}opts,regular expression 是使用 PCRE 格式
iex(1)> Regex.run ~r{[aeiou]}, "caterpillar"
["a"]
iex(2)> Regex.scan ~r{[aeiou]}, "caterpillar"
[["a"], ["e"], ["i"], ["a"]]
iex(3)> Regex.split ~r{[aeiou]}, "caterpillar"
["c", "t", "rp", "ll", "r"]
iex(4)> Regex.replace ~r{[aeiou]}, "caterpillar", "*"
"c*t*rp*ll*r"
Opt | Meaning |
---|---|
f | 強制該 pattern 要出現在多行資料的第一行 |
g | 支援命名的 groups |
i | case insensitive |
m | 如果有多行文字, ^ and $ 分別代表每一行文字的開始與結束。\A and \z 是這些文字的開頭與結束 |
s | 可使用 . 代表任意字元 |
U | 通常 * + 是 greedy,就是越多字元越好,但如果加上 U 就變成 ungreedy |
u | 可使用 unicode-specific patterns 例如 \p |
x | 可使用 extended mode—ignore whitespace and comments (# to end of line) |
- PID Ports
PID: a reference to a local or remote process
Port: a reference to an external resource,例如 C 的 library
- References
make_ref 會產生 a globally unique reference
- Tuples
tuple: an ordered collection of values
- { 1, 2 }
- { :ok, 42, "next" }
- { :error, :enoent }
通常 :ok 代表 noerror
iex(1)> {status, file} = File.open("hello.exs")
{:ok, #PID<0.86.0>}
iex(2)> {status, file} = File.open("hello1.exs")
{:error, :enoent}
- Lists
iex(3)> [ 1, 2, 3 ] ++ [ 4, 5, 6 ]
[1, 2, 3, 4, 5, 6]
iex(4)> [1, 2, 3, 4] -- [2, 4]
[1, 3]
iex(5)> 1 in [1,2,3,4]
true
iex(6)> "wombat" in [1, 2, 3, 4]
false
Keyword Lists: lists of key/value pairs,elixir 會轉換為 a list of two-value tuples:
[ name: "Dave", city: "Dallas", likes: "Programming" ]
[ {:name, "Dave"}, {:city, "Dallas"}, {:likes, "Programming"} ]
elixir 可省略 []
DB.save record, [ {:use_transaction, true}, {:logging, "HIGH"} ]
# 可寫成
DB.save record, use_transaction: true, logging: "HIGH"
以下為 list 與 tuple 的差異
iex(11)> [1, fred: 1, dave: 2]
[1, {:fred, 1}, {:dave, 2}]
iex(12)> {1, fred: 1, dave: 2}
{1, [fred: 1, dave: 2]}
- Map
%{ key => value, key2 => value2 }
當 key 為 atoms,可使用 . 直接取到該 value
iex(13)> states = %{ "AL" => "Alabama", "WI" => "Wisconsin" }
%{"AL" => "Alabama", "WI" => "Wisconsin"}
iex(14)> colors = %{ :red => 0xff0000, :green => 0x00ff00, :blue => 0x0000ff }
%{blue: 255, green: 65280, red: 16711680}
iex(15)> %{ "one" => 1, :two => 2, {1,1,1} => 3 }
%{:two => 2, {1, 1, 1} => 3, "one" => 1}
iex(16)> colors.red
16711680
iex(17)> colors[:red]
16711680
同時存在 Map 以及 keyword list 的原因:Map 的 key 必須是唯一的,但 keyword lists 的 key 可以重複
- Binary
可直接指定單一個 byte 內 不同寬度 bits 的數值
iex(1)> bin = << 1, 2 >>
<<1, 2>>
iex(2)> byte_size bin
2
iex(3)> bin = <<3 :: size(2), 5 :: size(4), 1 :: size(2)>>
<<213>>
iex(4)> :io.format("~-8.2b~n", :binary.bin_to_list(bin))
11010101
:ok
iex(5)> byte_size bin
1
- Dates and Times
Date 是 ISO-8601 格式
iex(6)> d1 = Date.new(2016, 12, 25)
{:ok, ~D[2016-12-25]}
iex(7)> {:ok, d1} = Date.new(2016, 12, 25)
{:ok, ~D[2016-12-25]}
iex(8)> d2 = ~D[2016-12-25]
~D[2016-12-25]
iex(9)> d1 == d2
true
iex(10)> inspect d1, structs: false
"%{__struct__: Date, calendar: Calendar.ISO, day: 25, month: 12, year: 2016}"
iex(15)> {:ok, t1} = Time.new(12, 34, 56)
{:ok, ~T[12:34:56]}
iex(16)> t2 = ~T[12:34:56]
~T[12:34:56]
iex(17)> t1==t2
true
iex(18)> inspect t2, structs: false
"%{__struct__: Time, calendar: Calendar.ISO, hour: 12, microsecond: {0, 0}, minute: 34, second: 56}"
iex(19)> inspect t1, structs: false
"{:ok, %{__struct__: Time, calendar: Calendar.ISO, hour: 12, microsecond: {0, 0}, minute: 34, second: 56}}"
iex(20)> t3 = ~T[12:34:56.78]
~T[12:34:56.78]
其他規則
elixir 的 identifier 可用大小寫 ASCII characters, digits 及 _,可用 ? 當結尾
module, record, protocol, and behavior names 是以大寫 letter 開頭
source code 使用 UTF-8 encoding
註解以 # 開頭
Boolean operations 有三種 true, false, and nil. nil is treated as false in Boolean contexts.
Operators
Comparison
a === b # strict equality (so 1 === 1.0 is false) a !== b # strict inequality (so 1 !== 1.0 is true) a == b # value equality (so 1 == 1.0 is true) a != b # value inequality (so 1 != 1.0 is false) a > b # normal comparison a >= b # : a < b # : a <= b # :
Boolean
a or b # true if a is true, otherwise b a and b # false if a is false, otherwise b not a # false if a is true, true otherwise
Relaxed Boolean
a || b # a if a is truthy, otherwise b a && b # b if a is truthy, otherwise a !a # false if a is truthy, otherwise true
Arithmetic operators
+ - * / div rem
Join operators
binary1 <> binary2 # concatenates two binaries list1 ++ list2 # concatenates two lists list1 -- list2 # removes elements of list2 from a copy of list 1
in operator
a in enum # tests if a is included in enum
Variable Scope: function body
with Expression
content = "Now is the time"
lp = with {:ok, file} = File.open("/etc/passwd"),
content = IO.read(file, :all),
:ok = File.close(file),
[_, uid, gid] = Regex.run(~r/_lp:.*?:(\d+):(\d+)/, content) do
"Group: #{gid}, User: #{uid}"
end
IO.puts lp # => Group: 26, User: 26
IO.puts content