Anonymous Functions
fn
parameter-list -> body
parameter-list -> body ...
end
iex(1)> sum = fn (a, b) -> a + b end
#Function<12.99386804/2 in :erl_eval.expr/5>
iex(2)> sum.(1, 2)
3
# 可省略 ()
iex(3)> sum = fn a, b -> a + b end
#Function<12.99386804/2 in :erl_eval.expr/5>
# 沒有參數的 fn
iex(5)> f2 = fn -> 99 end
#Function<20.99386804/0 in :erl_eval.expr/5>
iex(6)> f2.()
99
以 tuple 為參數
iex(8)> swap = fn { a, b } -> { b, a } end
#Function<6.99386804/1 in :erl_eval.expr/5>
iex(9)> swap.({1,2})
{2, 1}
一個 fn 可以有多種 function body,fn 是以 File.open 回傳的結果決定 fn body 的內容是使用哪一個 pattern 的定義
iex(1)> open_file = fn
...(1)> {:ok, file} -> "Read data: #{IO.read(file, :line)}"
...(1)> {_, error} -> "Error: #{:file.format_error(error)}"
...(1)> end
#Function<6.99386804/1 in :erl_eval.expr/5>
iex(2)> open_file.(File.open("/etc/passwd"))
"Read data: ##\n"
iex(3)> open_file.(File.open("nonexistent"))
"Error: no such file or directory"
可以 Return Function
iex(6)> fun1 = fn -> fn -> "Hello" end end
### 等同 fun1 = fn -> (fn -> "Hello" end) end
#Function<20.99386804/0 in :erl_eval.expr/5>
iex(7)> fun1.()
#Function<20.99386804/0 in :erl_eval.expr/5>
iex(8)> fun1.().()
"Hello"
Functions Remember Their Original Environment
iex(13)> greeter = fn name -> (fn -> "Hello #{name}" end) end
#Function<6.99386804/1 in :erl_eval.expr/5>
iex(14)> dave_greeter = greeter.("Dave")
#Function<20.99386804/0 in :erl_eval.expr/5>
iex(15)> dave_greeter.()
"Hello Dave"
Parameterized Functions
iex(19)> add_n = fn n -> (fn other -> n + other end) end
#Function<6.99386804/1 in :erl_eval.expr/5>
iex(20)> add_two = add_n.(2)
#Function<6.99386804/1 in :erl_eval.expr/5>
iex(21)> add_five = add_n.(5)
#Function<6.99386804/1 in :erl_eval.expr/5>
iex(22)> add_two.(3)
5
iex(23)> add_five.(7)
12
以 fun 為參數
iex(26)> times_2 = fn n -> n * 2 end
#Function<6.99386804/1 in :erl_eval.expr/5>
iex(27)> apply = fn (fun, value) -> fun.(value) end
#Function<12.99386804/2 in :erl_eval.expr/5>
iex(28)> apply.(times_2, 6)
12
利用 Pin operator 將 fun 固定參數
defmodule Greeter do
def for(name, greeting) do
fn
(^name) -> "#{greeting} #{name}"
(_) -> "I don't know you"
end
end
end
mr_greeter = Greeter.for("Test", "You")
IO.puts mr_greeter.("Test") # => You Test
IO.puts mr_greeter.("Dave") # => I don't know you
利用 & 實作 func
add_one = &(&1 + 1)
# & 就是 fn
# &1 是 fn 的第一個參數, &2 是 fn 的第二個參數
### 等同
add_one = fn (n) -> n + 1 end
iex(38)> divrem = &{ div(&1,&2), rem(&1,&2) }
#Function<12.99386804/2 in :erl_eval.expr/5>
iex(39)> divrem.(13, 5)
{2, 3}
iex(42)> l = &length/1
&:erlang.length/1
iex(43)> l.([1,3,5,7])
4
& 可快速產生一個 anonymous function 傳入 map function 的第二個參數
iex(44)> Enum.map [1,2,3,4], &(&1 + 1)
[2, 3, 4, 5]
Modules & Named Functions
times.exs
defmodule Times do
def double(n) do
n * 2
end
end
$ iex times.exs
iex(1)> Times.double
double/1
iex(1)> Times.double(3)
6
也可以啟動 iex 後,用 c 編譯
iex(1)> c("times.exs")
[Times]
## 可直接知道是哪一行,哪一個 function 發生錯誤
iex(2)> Times.double("2")
** (ArithmeticError) bad argument in arithmetic expression
times.exs:3: Times.double/1
do: 是 syntax sugar,可以用 () 將 expression grouping 在一起, do: 就是 do ... end 的簡化語法
defmodule Times do
def double(n), do: n * 2
def greet(greeting, name), do: (
IO.puts greeting
IO.puts "How're you doing, #{name}?"
)
end
Function Call with Pattern Matching
defmodule Factorial do
def of(0), do: 1
def of(n), do: n * of(n-1)
end
iex(1)> Factorial.of(3)
6
iex(2)> Factorial.of(100)
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
def of(0), do: 1 不能寫在後面,因為 elixir 是依照順序檢查是否有符合 pattern,如果寫錯了,compiler 會出現警告
defmodule BadFactorial do
def of(n), do: n * of(n-1)
def of(0), do: 1
end
warning: this clause cannot match because a previous clause at line 2 always matches
factorial1-bad.exs:3
Guard Clauses
defmodule Guard do
def what_is(x) when is_number(x) do
IO.puts "#{x} is a number"
end
def what_is(x) when is_list(x) do
IO.puts "#{inspect(x)} is a list"
end
def what_is(x) when is_atom(x) do
IO.puts "#{x} is an atom"
end
end
Guard.what_is(99) # => 99 is a number
Guard.what_is(:cat) # => cat is an atom
Guard.what_is([1,2,3]) # => [1,2,3] is a list
回頭看剛剛的 factorial,如果 n 為負數,就會發生無窮迴圈的狀況,因此要改為
defmodule Factorial do
def of(0), do: 1
def of(n) when n > 0 do
n * of(n-1)
end
end
在 guard clause 中可以使用的 elixir expression
comparison operator
==, !=, ===, !==, >, <, <=, >=
Boolean and negation operators
or, and, not, ! 不能使用 || and &&
Arithmetic operators
+, -, *, /
Join operators
<> and ++, as long as the left side is a literal.
in operator
Membership in a collection or range
Type-check functions
is_atom is_binary is_bitstring is_boolean is_exception is_float is_function is_integer is_list is_map is_number is_pid is_port is_record is_reference is_tuple
Other functions
回傳 value 的 built-in functions
abs(number) bit_size(bitstring) byte_size(bitstring) div(number,number) elem(tuple, n) float(term) hd(list) length(list) node() node(pid|ref|port) rem(number,number) round(number) self() tl(list)trunc(number) tuple_size(tuple)
參數的預設值
defmodule Example do
def func(p1, p2 \\ 2, p3 \\ 3, p4) do
IO.inspect [p1, p2, p3, p4]
end
end
Example.func("a", "b") # => ["a",2,3,"b"]
Example.func("a", "b", "c") # => ["a","b",3,"c"]
Example.func("a", "b", "c", "d") # => ["a","b","c","d"]
因為前面已經定義了 2, 3, 4 個參數的 func,因此 def func(p1, p2) 會出現重複 func 定義的 error
defmodule Example do
def func(p1, p2 \\ 2, p3 \\ 3, p4) do
IO.inspect [p1, p2, p3, p4]
end
def func(p1, p2) do
IO.inspect [p1, p2]
end
end
** (CompileError) default_params.exs:7: def func/2 conflicts with defaults from def func/4
default_params.exs:7: (module)
default_params.exs:1: (file)
以沒有 func body 的 func 定義,來解決剛剛遇到的問題
defmodule Params do
def func(p1, p2 \\ 123)
def func(p1, p2) when is_list(p1) do
"You said #{p2} with a list"
end
def func(p1, p2) do
"You passed in #{p1} and #{p2}"
end
end
IO.puts Params.func(99) # You passed in 99 and 123
IO.puts Params.func(99, "cat") # You passed in 99 and cat
IO.puts Params.func([99]) # You said 123 with a list
IO.puts Params.func([99], "dog") # You said dog with a list
defp 可定義 Private Functions,但不能 private 與 public function 不能共用
Pipe Operator |>
以往會寫成
people = DB.find_customers
orders = Orders.for_customers(people)
tax = sales_tax(orders, 2016)
filing = prepare_filing(tax)
或是
filing = prepare_filing(sales_tax(Orders.for_customers(DB.find_customers), 2016))
但有了 |> 可改成
filing = DB.find_customers
|> Orders.for_customers
|> sales_tax(2016)
|> prepare_filing
val |> f(a,b)
等同
f(val,a,b)
list
|> sales_tax(2016)
|> prepare_filing
等同
prepare_filing(sales_tax(list, 2016))
& 就是 fun
iex> (1..10) |> Enum.map(&(&1*&1)) |> Enum.filter(&(&1 < 40))
[1, 4, 9, 16, 25, 36]
Modules
defmodule Mod do
def func1 do
IO.puts "in func1"
end
def func2 do
func1
IO.puts "in func2"
end
end
Mod.func1
Mod.func2
Inner Module
defmodule Outer do
defmodule Inner do
def inner_func do
end
end
def outer_func do
Inner.inner_func
end
end
Outer.outer_func
Outer.Inner.inner_func
mix/tasks/doctest.ex
defmodule Mix.Tasks.Doctest do
def run do
end
end
Mix.Tasks.Doctest.run
Directives for Modules
import
語法
import Module [, only:|except: ]
ex:
import List, only: [ flatten: 1, duplicate: 2 ]
defmodule Example do def func1 do List.flatten [1,[2,3],4] end def func2 do import List, only: [flatten: 1] flatten [5,[6,7],8] end end
alias
defmodule Example do def compile_and_go(source) do alias My.Other.Module.Parser, as: Parser alias My.Other.Module.Runner, as: Runner source |> Parser.parse() |> Runner.execute() end end
也可寫成
alias My.Other.Module.Parser alias My.Other.Module.Runner alias My.Other.Module.{Parser, Runner}
require
當寫下 require a module,在編譯時可保證一定存在該 macrio 定義
Module Attribute
只能用在 module 的最底層,這不是變數,比較接近 configuration, metadata 的用途
defmodule Example do
@author "Test"
def get_author do
@author
end
@attr "one"
def first, do: @attr
@attr "two"
def second, do: @attr
end
module name 就是 atoms,因此 IO 會被轉換為 Elixir.IO
iex(1)> is_atom IO
true
iex(2)> to_string IO
"Elixir.IO"
iex(3)> :"Elixir.IO" === IO
true
iex(4)> IO.puts 123
123
:ok
iex(5)> :"Elixir.IO".puts 123
123
:ok
如何呼叫 Erlang Library 的 Functions
erlang 的 io module,就是 elixir 的一個 atom :io
iex(16)> :io.format("The number is ~3.1f~n", [5.678])
The number is 5.7
沒有留言:
張貼留言