Use Erlc Judge Loop State
通过erlc判断递归函数是否为尾递归
使用erlc编译erlang source code为字节码,通过字节码的特征来判断这个递归是否为尾递归
erlc的相关介绍可以到erlang的文档中找到,这里就不细说。这里主要会使用到的就是 -S
选项。直接上代码
-module(test_file). -export([test1/1, test2/1, test_case/1]). test1([Head|Tail]) -> test1(Tail); test1([]) -> ok. test2([Head|Tail]) -> test2(Tail), ok; test2([]) -> ok. test_case([Head|Tail]) -> case Head of Head when is_integer(Head) -> test_case(Tail); _ -> ok end; test_case([]) -> ok.
使用erlc -S生成的字节码
{module, test_file}. %% version = 0 {exports, [{module_info,0},{module_info,1},{test1,1},{test2,1},{test_case,1}]}. {attributes, []}. {labels, 15}. {function, test1, 1, 2}. {label,1}. {line,[{location,"e:/GitHub/CODE_T~1/test_file.erl",6}]}. {func_info,{atom,test_file},{atom,test1},1}. {label,2}. {test,is_nonempty_list,{f,3},[{x,0}]}. {get_list,{x,0},{x,1},{x,0}}. {call_only,1,{f,2}}. {label,3}. {test,is_nil,{f,1},[{x,0}]}. {move,{atom,ok},{x,0}}. return. {function, test2, 1, 5}. {label,4}. {line,[{location,"e:/GitHub/CODE_T~1/test_file.erl",11}]}. {func_info,{atom,test_file},{atom,test2},1}. {label,5}. {test,is_nonempty_list,{f,6},[{x,0}]}. {allocate,0,1}. {get_list,{x,0},{x,1},{x,0}}. {line,[{location,"e:/GitHub/CODE_T~1/test_file.erl",12}]}. {call,1,{f,5}}. {move,{atom,ok},{x,0}}. {deallocate,0}. return. {label,6}. {test,is_nil,{f,4},[{x,0}]}. {move,{atom,ok},{x,0}}. return. {function, test_case, 1, 8}. {label,7}. {line,[{location,"e:/GitHub/CODE_T~1/test_file.erl",18}]}. {func_info,{atom,test_file},{atom,test_case},1}. {label,8}. {test,is_nonempty_list,{f,10},[{x,0}]}. {get_list,{x,0},{x,1},{x,2}}. {test,is_integer,{f,9},[{x,1}]}. {move,{x,2},{x,0}}. {call_only,1,{f,8}}. {label,9}. {move,{atom,ok},{x,0}}. return. {label,10}. {test,is_nil,{f,7},[{x,0}]}. {move,{atom,ok},{x,0}}. return. {function, module_info, 0, 12}. {label,11}. {line,[]}. {func_info,{atom,test_file},{atom,module_info},0}. {label,12}. {move,{atom,test_file},{x,0}}. {line,[]}. {call_ext_only,1,{extfunc,erlang,get_module_info,1}}. {function, module_info, 1, 14}. {label,13}. {line,[]}. {func_info,{atom,test_file},{atom,module_info},1}. {label,14}. {move,{x,0},{x,1}}. {move,{atom,test_file},{x,0}}. {line,[]}. {call_ext_only,2,{extfunc,erlang,get_module_info,2}}.
由此可见,当函数调用中使用的是一般的call的时候,这个递归就是非尾递归的。是会有堆栈的消耗的。当函数的调用是call_only时,说明就是尾递归的。