最近接触到一个函数叫ets:fun2ms/1,因为不了解被坑了,来解析下他的用法和常用情景
基本含义
把语法函数转为匹配规范的伪函数,听到这个可能就蒙了,有啥用啊
使用一个解析转换的仿函数 ListeralFun 作为参数传递给该函数转换出一个匹配规范。“literal” 意味着函数必须以的文本的形式作为函数的参数,它不能是依次传递给函数的变量。
需要注意的是,使用这个fun2ms的函数必须在代码文件前面加入这一行,
否则会报这个错,我当时就找了半天bug

加了的话就会编译正常
示例
2> ets:fun2ms(fun({M, N}) when N > 3 -> M end).
[{{'$1','$2'},[{'>','$2',3}],['$1']}]
这里可以看到函数返回的是一个匹配规范,第一个参数第二个参数,大于号、第二个参数、具体的3,返回第一个参数
3> X = 3,
ets:fun2ms(fun({M, N}) when N > X -> M end).
[{{'$1','$2'},[{'>','$2',{const,3}}],['$1']}]
也可以用外部变量,const就是全局常量
4> ets:fun2ms(fun({M, N}) when N > 3, is_atomm(M) -> M end).
* 1: illegal guard expression
这里就报错了,因为局部函数或全局函数是不能出现在函数的断言里的
5> ets:fun2ms(fun({M, N}) when N > 3, is_atom(M) -> M end).
[{{'$1','$2'},[{'>','$2',3},{is_atom,'$1'}],['$1']}]
内置函数可以被匹配规范函数调用
延伸
这里的4、5行里when后面有一种可以一种不可以,这个在之前我有个朋友出现了类似的问题并且和我请教,当时印象有点深,因为这个点我也很模糊。when后面不能接自己写的函数,但是内置的函数可以做条件
比如:
这里用了内建函数,但是编译的时候不能知道结果所以也不行,可以发现下面的代码在IDEA里敲完就直接报错了
导师的解释是when在匹配后面必须要有确定的结果,编译的时候就能知道结果的运算或者是NIF(Native Implemented Function)的。
NIF补充知识
在Erlang中,NIF(Native Implemented Function)被用来扩展erlang的某些功能,一般用来实现一些erlang很难实现的,或者一些erlang实现效率不高的功能。
NIF使用C开发,效率和C接近,比纯erlang实现要高。NIF会编译成动态库,直接动态加载到erlang进程空间调用,也是erlang扩展新方法最高效的做法。调用NIF不用上下文的切换开销,但是也有代价,NIF的crash会导致整个Erlang进程crash。
我的理解是与NIF对立的就是我们熟悉的BIFs(Build-in Functions)内建函数,内建函数就是比如trunc直接erlang:trunc就可以调用的,常见的内建函数:
函数名 | 功能 | 函数名 | 功能 |
---|---|---|---|
trunc | 取整数部分 | round | 四舍五入 |
float | 转化为浮点数 | is_atom | 判断是否为原子 |
is_tuple | 判断是否为元组 | hd/1 | 返回列表的第一个元素 |
tl/1 | 返回列表的最后一个元素 | length/1 | 返回列表的长度 |
tuple_size/1 | 返回元组大小 | element/1 | 返回第n个元组的元素 |
函数名 | 功能 |
---|---|
setelement/3 | 替换元组中的某一个元素,并返回新元组,setelement(要替换原子的位置,元组名,新原子的值) |
erlang:append_element/2 | 添加一个原子到元组的末尾。(元组名,新原子的值) |
类型转换 | |
atom_to_list | 原子转化为列表->字符串 |
list_to_atom | 列表转化为原子 |
integer_to_list | 整数转化为列表 |
list_to_tuple | tuple_to_list |
float,list_to_float | float_to_list,integer_to_list |
那这里后面到底可以写什么?
- 判断数据类型(内建函数):is_binary, is_atom, is_boolean, is_tuple
- 比较运算符:==, =/=, <, >…
- 判断语句: not, and, xor…
可以发现没报错的
可以发现复杂点的还是会报错
回到fun2ms使用用法
和select搭配
我们发现ets:select/2中的参数为select(
Tab
,
MatchSpec
) -> [
Match
],这里第二个参数可不就是咱fun2ms的返回值吗,
这不冲他
示例
1> ets:new(tab,[set,named_table]).
tab
2> ets:insert(tab,[{apple,1},{banana,2},{orange,3}]).
true
3> ets:fun2ms(fun({Key, Value} = Object) when Key =:= apple -> Object end).
[{{'$1','$2'},[{'=:=','$1',apple}],['$_']}]
4> Ms = ets:fun2ms(fun({Key, Value} = Object) when Key =:= apple -> Object end).
[{{'$1','$2'},[{'=:=','$1',apple}],['$_']}]
5> ets:select(tab, Ms).
[{apple,1}]
6>
我对这个fun2ms的返回值的理解是,提前给条件做一个判断,这里就是先设置Key=apple的类型的,然后匹配Key、Value类型来做判断,再作为select第二个参数传入,发现确实能匹配出对应Key==apple类型的
和match_spec_compile搭配
反正冷门了,不怕再冷门点,我们查API发现ets:match_spec_compile/1这个函数,传入的参数也是MatchSpec,咋说,试下呗
match_spec_compile(MatchSpec) -> CompiledMatchSpec
测试发现返回一个不认识的东西
1> MatchSpec = ets:fun2ms(fun({M, N}) when N > 3 -> M end),
ets:match_spec_compile(MatchSpec).
#Ref<0.1118727578.437125123.105226>
查表解释为这个函数将一个匹配规范MatchSpec转换为一个可用于ets:match_sprc_run/2在后续调用的内部表示形式。转换后的内部形式“不透明”的一个数据,并不能转回为其原来的外部数据格式,并且完好无损的转回。
示例
1> MatchSpec = ets:fun2ms(fun({M, N}) when N > 3 -> M end),
CompileMatchSpec = ets:match_spec_compile(MatchSpec).
#Ref<0.2741862248.2048524291.40502>
2> List = [{1,2},{3,4},{5,6}].
[{1,2},{3,4},{5,6}]
3> ets:match_spec_run(List,CompileMatchSpec).
[3,5]
4>
越兜越复杂了哈,先看懂吧~
和is_compile_ms的搭配
还有个一起说下吧,这个函数主要是检查已编译的匹配描述是否是有效的,
is_compiled_ms(Term) -> boolean()
示例
1> MatchSpec = ets:fun2ms(fun({M, N}) when N > 3 -> M end).
[{{'$1','$2'},[{'>','$2',3}],['$1']}]
2> Term = ets:match_spec_compile(MatchSpec).
#Ref<0.63853007.1514536963.50818>
3> ets:is_compiled_ms(Term).
true
4> ets:is_compiled_ms(MatchSpec).
false
5>
可以看到只有符合要求的返回true,其他任意类型均返回false