天天看点

Erlang_ets冷门函数fun2ms

最近接触到一个函数叫ets:fun2ms/1,因为不了解被坑了,来解析下他的用法和常用情景

基本含义

把语法函数转为匹配规范的伪函数,听到这个可能就蒙了,有啥用啊

使用一个解析转换的仿函数 ListeralFun 作为参数传递给该函数转换出一个匹配规范。“literal” 意味着函数必须以的文本的形式作为函数的参数,它不能是依次传递给函数的变量。

需要注意的是,使用这个fun2ms的函数必须在代码文件前面加入这一行,

否则会报这个错,我当时就找了半天bug

Erlang_ets冷门函数fun2ms

加了的话就会编译正常

示例

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里敲完就直接报错了

Erlang_ets冷门函数fun2ms

导师的解释是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…

可以发现没报错的

Erlang_ets冷门函数fun2ms
Erlang_ets冷门函数fun2ms

可以发现复杂点的还是会报错

Erlang_ets冷门函数fun2ms

回到fun2ms使用用法

和select搭配

我们发现ets:select/2中的参数为select(

Tab

,

MatchSpec

) -> [

Match

],这里第二个参数可不就是咱fun2ms的返回值吗,

Erlang_ets冷门函数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