- 匿名函数用 fn 关键字创建 end 结束,定义的时候绑定给一个变量
//iex命令下
sum = fn(a,b) -> a + b end
- 如果函数不接受参数,调用的时候还是要用到圆括号
//定义
iex> greet = fn -> IO.puts "hello" end
//调用
iex> greet.()
//输出
hello
:ok
- 可以在函数定义中省略圆括号
iex> f1 = a,b -> a * b end
函数和模式匹配
Elixir没有赋值的概念,而是将值与模式进行匹配
//调换元组中两个元素的顺序
iex> swap = fn {a,b} -> {b,a} end
//调用
iex> swap.({6,8})
//结果
{8,6}
一个函数,多个函数体
iex(1)> handle_open = fn
...(1)> { :ok,file } -> "Read data:#{ IO.read(file,:line)}"
...(1)> {_,error} -> "error:#{:file.format_error(error)}"
...(1)> end
#Function<6.128620087/1 in :erl_eval.expr/5>
iex(2)> handle_open.(File.open("code/intro/hello.exs"))
"error:no such file or directory"
iex(3)>
2,3行 定义两个独立的函数,每个函数接受一个元组作为参数。第一个函数要求元组的首元素是:ok。
第2个函数使用特殊变量 _ (下画线)
来匹配任意值的首元素
上述代码中
:file.format_error
:file
是底层Erlang的File模块,所以我们可以调用file.format_err
函数
File.open
则是 Elixir 内建的File模块
- 上述代码还用到了Elixir的字符串插值:在字符串里,
#{。。。}
里的内容会被求值,并且求值结果会被替换为字符串。
- 用ide 编辑成exs文件运行
//保存为handle_open.exs
handle_open = fn
{:ok,file} -> "First line: #{IO.read(file,:line)}"
{_,error} -> "Error: #{:file.format_error(error)}"
end
IO.puts handle_open.(File.open("nonexistent")) # 传入一个不存在的文件
调用
iex(1)> pwd
/Users/matsu/Documents/workspace/workspace_elixir
iex(2)> c "handle_open.exs"
Error: no such file or directory
[]
iex(3)>
或用elixir调用
$ elixir handle_open.exs
Error: no such file or directory
.exs
指的是脚文件,可以直接从源文件运行
.ex
是编译之后再使用的代码
能返回函数的函数(闭包)
示例:
iex(2)> prefix = fn st1->(
...(2)> fn st2->"#{st1} #{st2 }" end
...(2)> )
...(2)> end
#Function<6.128620087/1 in :erl_eval.expr/5>
iex(3)> mrs = prefix.("Mrs")
#Function<6.128620087/1 in :erl_eval.expr/5>
iex(4)> mrs.("Smith")
"Mrs Smith"
iex(5)> prefix.("hello").("steven qin")
"hello steven qin"
iex(6)>
将函数作为参数来传递
iex(1)> times_2 = fn n -> n*2 end
#Function<6.128620087/1 in :erl_eval.expr/5>
iex(2)> apply = fn (fun,value) -> fun.(value) end
#Function<12.128620087/2 in :erl_eval.expr/5>
iex(3)> apply.(times_2,6)
12
iex(4)>
内建的Enum模块有个名为map的函数,它接受两个参数:
收集collection
和一个函数
。它返回一个列表
,是将收集
中的每个元素应用于那个函数
之后所得的结果。这是一个例子
iex(4)> list = [1,3,5,7,9]
[1, 3, 5, 7, 9]
iex(5)> Enum.map list , fn elem -> elem * 2 end
[2, 6, 10, 14, 18]
iex(6)> Enum.map list , fn elem -> elem * elem end
[1, 9, 25, 49, 81]
iex(7)> Enum.map list , fn elem -> elem > 6 end
[false, false, false, true, true]
iex(8)>
& 函数捕获运算符
&
运算符将跟在其后的表达式转换成函数,在表达式内部,占位符&1 &2
等对应第一个,第二个及接下来的函数参数;所以,&(&1 + &2)
会被转换成fn p1,p2 -> p1 + p2 end
iex(10)> add_one = &(&1 + 1)
#Function<6.128620087/1 in :erl_eval.expr/5>
iex(11)> add_one.(44)
45
iex(12)> square = &(&1 * &1)
#Function<6.128620087/1 in :erl_eval.expr/5>
iex(13)> square.(6)
36
iex(14)> speak = &(IO.puts(&1))
&IO.puts/1
iex(15)> speak.("hello steven")
hello steven
:ok
iex(16)>
&(IO.puts(&1))
等同fn x -> IO.puts(x) end
elixir 会把这样的匿名函数优化为IO.puts/1
- 要保证这种方式能正确的工作,参数必须以正确的顺序排列
iex(18)> rnd = &(Float.round(&1,&2))
&Float.round/2
iex(19)> rnd = &(Float.round(&2,&1))
#Function<12.128620087/2 in :erl_eval.expr/5>
iex(20)>
elixir 里的
[] {}
是运算符,所以字面量的列表
和元组
也可以转换成函数。下面函数返回包含两个整数相除得到的商和余数
iex(23)> divrem = &{ div(&1,&2) , rem(&1,&2) }
#Function<12.128620087/2 in :erl_eval.expr/5>
iex(24)> divrem.(13,5)
{2, 3}
iex(25)>
&
函数捕获运算符还有一种用法:可能给它一个已经存在的函数和形参数量
,它返回一个调用该函数的匿名函数
。
iex(28)> l = &length/1
&:erlang.length/1
iex(29)> l.([ 1,3,5,7])
4
iex(30)> len = &Enum.count/1
&Enum.count/1
iex(31)> len.([ 1,2,3,4 ])
4
iex(32)> m = &Kernel.min/2 # 这是Erlang函数的一个别名
&:erlang.min/2
iex(33)> m.( 99,88)
88
iex(34)>
&
快捷方式是一种将函数传递给其他函数的极好方法
iex(36)> Enum.map [1,2,3,4],&(&1 + 1)
[2, 3, 4, 5]
iex(37)> Enum.map [1,2,3,4],&(&1 * &1)
[1, 4, 9, 16]
iex(38)> Enum.map [1,2,3,4],&(&1 < 3)
[true, true, false, false]
iex(39)>
函数是核心
编程的基础是转换数据。函数是实现这个转换的小引擎。