Elixir程序设计笔记(5匿名函数)

  • 匿名函数用 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)>

函数是核心

编程的基础是转换数据。函数是实现这个转换的小引擎。

你可能感兴趣的:(Elixir程序设计笔记(5匿名函数))