最近接觸到一個函數叫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