2010-06-18 72 views
3

我有一個簡單的記錄結構,包含一個頭(H)和一個數據行(D)1:N的列表。所有標題行必須以數字開頭。所有的數據行都有一個前導空白。也可能有一些空行(E)必須忽略。Erlang - 列表解析 - 填充記錄

L = [H, D, D, E, H, D, E, H, D, D, D]. 

我想創建一個記錄列表:

-record(posting,{header,data}). 

使用列表理解。最好的辦法是做什麼?

回答

3

你應該做這樣的事情:

make_records(L) when is_list(L) -> 
    F = fun([32|_]=D,{#posting{}=H,Acc}) -> {H,[H#posting{data=D}|Acc]}; 
     ([], Acc) -> Acc; 
     ([F|_]=H, {_,Acc}) when F=<$0, F>=$9 -> {#posting{header=>H}, Acc} 
     end, 
    {_, R} = lists:foldl(F, {undefined, []}, L), 
    R. 

無論如何,我認爲簡單的Erlang版本似乎並不過於複雜的,應快一點。

make_records2(L) when is_list(L) -> 
    make_records2(L, undefined, []). 

make_records2([], _, R) -> R; 
make_records2([[32|_]=D|T], H, Acc) when is_list(H) -> 
    make_records2(T, H, [#posting{header=H,data=D}|Acc]); 
make_records2([[]|T], H, Acc) -> 
    make_records2(T, H, Acc); 
make_records2([[F|_]=H|T], _, Acc) when F>=$0, F=<$9 -> 
    make_records2(T, H, Acc). 

編輯:如果您有更好的添加一行分類或對其進行解析,增加新的功能更好,因爲它提高了可讀性。

parse_row([Digit|_]=R) when Digit >= $0, Digit =< $9 -> {header, R}; 
parse_row(R) -> try_spaces(R). 

try_spaces([]) -> empty; 
try_spaces([Sp|R]) when Sp=:=$\s; Sp=:=$\t; Sp=:=$\n -> 
    try_spaces(R); % skip all white spaces from Data field 
try_spaces(Data) -> {data, Data}. 

您可以使用它像這樣:

make_records(L) when is_list(L) -> 
    F = fun(Row, {H, Acc}) -> 
      case parse_row(Row) of 
      {data, D} when is_record(H, posting) -> {H,[H#posting{data=D}|Acc]}; 
      empty -> Acc; 
      {header, H} -> {#posting{header=>H}, Acc} 
     end, 
    {_, R} = lists:foldl(F, {undefined, []}, L), 
    R. 

尾遞歸本地二郎解決方案:

make_records2(L) when is_list(L) -> 
    make_records2([parse_row(R) || R<-L], undefined, []). 

make_records2([], _, R) -> R; 
make_records2([{data, D}|T], H, Acc) when is_list(H) -> 
    make_records2(T, H, [#posting{header=H,data=D}|Acc]); 
make_records2([empty|T], H, Acc) -> 
    make_records2(T, H, Acc); 
make_records2([{header,H}|T], _, Acc) -> 
    make_records2(T, H, Acc). 

我認爲有從性能的角度來看沒有理由使用尾遞歸:

make_records3(L) when is_list(L) -> 
    make_records3(L, undefined). 

make_records3([], _) -> []; 
make_records3([R|T], H) -> 
    case parse_row(R) of 
    {data, D} when is_list(H) -> [#posting{head=H,data=D}|make_records3(T, H)]; 
    empty -> make_records3(T, H); 
    {header, H2} -> make_records3(T, H2) 
    end. 

...以及許多其他變體。

6

在這種情況下,您必須使用列表:foldl/3而不是列表解析。隨着與foldl/3,您可以通過整個列表積累頭的值和數據L.

0

我需要摺疊標題下的所有數據線 - 所以這裏的那一刻是我:

sanitize(S) -> trim:trim(S). 

    make_records(L) when is_list(L) -> make_records(L, undefined, []). 

    make_records([], _, R) -> lists:reverse(R); 

    make_records([[32|_]=D|T], H, Acc) when is_tuple(H) -> 
     make_records(T, {element(1,H),[sanitize(D)|element(2,H)]},Acc); 

    make_records([[$\n|_]=D|T], H, Acc) when is_tuple(H) -> 
     make_records(T, H, Acc); 


    make_records([[F|_]=H|T], B, Acc) when F>=$0, F=<$9 -> 
     if is_tuple(B) -> 
      make_records(T, {sanitize(H),[]}, [#posting{header=element(1,B), 
      data=lists:reverse(element(2,B))}|Acc]); 
     true -> 
      make_records(T, {sanitize(H),[]}, Acc) 
     end.