Erlang中删除列表元素在标准模块lists中可以找到delete/2函数,
比如调用lists:delete(2, [1,2,3,4,5])后将返回新的列表[1,3,4,5]
笔者在翻阅lists模块源码中发现,一些函数实现成BIF,比如reverse就是一个BIF,在注释中发现
%% reverse(L) reverse all elements in the list L. Is now a BIF!
由此确定reverse是一个BIF。
但对delete函数的实现没找到类似的注释,怀疑其不是一个BIF,其实现存在性能问题,其实现代码如下:
delete(Item, [Item|Rest]) -> Rest; delete(Item, [H|Rest]) -> [H|delete(Item, Rest)]; delete(_, []) -> [].
这个实现没使用尾递归,对大表的操作将会导致堆栈上的内存消耗严重。
测试程序如下:
test1() -> {ok,Bin} = file:read_file("file1.txt"), L = binary_to_list(Bin), R = lists:delete($a,L), io:format("~p~n",[length(R)]).
文件file1.txt有近30M大小,运行时内存高值1G以上。
笔者对delete重新用尾递归的方式改写一遍:
delete(E, [E|T], R) -> lists:reverse(R) ++ T; delete(E, [H|T], R) -> delete(E, T, [H|R]); delete(E, [], R) -> lists:reverse(R).
再运行测试程序:
test2() -> {ok,Bin} = file:read_file("file1.txt"), L = binary_to_list(Bin), R = delete($a,L,[]), io:format("~p~n",[length(R)]).
发现内存占用在800M以内。
由此可以推测标准模块中lists的delete函数存在性能优化的空间,其他函数可能也同样存在这个问题,这对小列表不存在什么问题,
对大列表的处理如果内存足够也没什么问题,但总会让人觉得不爽。同时也说明编写尾递归函数的重要性,如果是自定义函数,写成尾递归的形式总是对的。