2016-03-28 48 views
0

所以我写了两个版本的gen_fsm来解析一个字节流。我正在寻找一个以字符串snp开头的数据包,然后我想在p之后保存下一个20个字节。这段代码只关心查找标题。我需要一些建议,指出哪一种是更加惯用的Erlang或更好的写法。哪一种比较习惯?

选项1

parse_header({parse, Byte}, {Header, [Next | Rest]}) -> 
    case Byte of 
    Next when length(Rest) > 0 -> {next_state, parse_header, {Header, Rest}}; 
    Next when length(Rest) == 0 -> {next_state, parse_data, []}; 
    $s  -> parse_header({parse, Byte}, {Header, Header}); 
    _   -> {next_state, parse_header, {Header, Header}} 
end. 

选项2

parse_start({parse, Byte}, State) when Byte == $s -> 
    {next_state, parse_new, State}; 
parse_start({parse, Byte}, State) when Byte /= $s -> 
    {next_state, parse_start, State}. 

parse_new({parse, Byte}, State) when Byte == $n -> 
    {next_state, parse_packet, State}; 
parse_new({parse, Byte}, State) when Byte == $s -> 
    parse_start({parse, Byte}, State); 
parse_new({parse, _Byte}, State) -> 
    {next_state, parse_start, State}. 

parse_packet({parse, Byte}, State) when Byte == $p -> 
    {next_state, parse_data, State}; 
parse_packet({parse, Byte}, State) when Byte == $s -> 
    parse_start({parse, Byte}, State); 
parse_packet({parse, _Byte}, State) -> 
    {next_state, parse_start, State}. 

回答

1

两种实现都很好,但是你应该更喜欢这最小化趟gen_fsm环的实现中,从而解析数据详尽直到没有别的东西需要解析,然后才将控制权返回给gen_fsm

所以在这方面的第一个实施会更好。另外,较短的实现通常更容易推理,因此第一个解决方案再次是一个要点。

但是,第二种实现(可以说)更清晰,更适合gen_fsm方法,因此如果需要可能更容易维护和扩展。

也许你可以找到一个解决方案,从两个最好的部分?例如,这个怎么样:

parse_header([ $s, $n, $p | Rest]) -> copy_data(Rest, 20, []); 
parse_header([ _ | T ]) -> parse_header(T); 
parse_header(List) when length(List) < 3 -> {next_state, parse_header}. 

copy_data(_, 0, Acc) -> lists:reverse(Acc); 
copy_data([], X, Acc) -> {next_state, {copy_data, X, Acc}}; 
copy_data([H | T], X, Acc) -> copy_data(T, X - 1, [H | Acc]). 

它试图详尽地读取输入,并且仅返回控制权交还给gen_fsm如果没有更多的数据读取。就像在第二个解决方案中一样,它将解析标题与读取数据分开。

这当然只是一个例子,如何解决问题,因为我不知道你的其他代码和你的确切需求。