Erlang - список понимания - заполнение записей - PullRequest
3 голосов
/ 18 июня 2010

У меня есть простая структура записи, состоящая из заголовка (H) и списка строк данных (D) 1: N. Все строки заголовка должны начинаться с цифры. Все строки данных имеют начальные пробелы. Между ними также могут быть пустые строки (E), которые следует игнорировать.

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

Я хотел бы создать список записей:

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

с использованием списка понимания. Какой лучший способ сделать это?

Ответы [ 3 ]

6 голосов
/ 18 июня 2010

В этом случае вы должны использовать списки: foldl / 3 вместо списочных представлений. С помощью foldl / 3 вы можете накапливать значения заголовка и данных через весь список L.

3 голосов
/ 18 июня 2010

Вы должны сделать что-то вроде этого:

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.

Хвостовое рекурсивное решение Erlang:

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.

... и многих других вариантов..

0 голосов
/ 20 июня 2010

Мне нужно было свернуть все строки данных под заголовком - на данный момент вот что у меня есть:

  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.
...