Erlang - 列表推导式 - 填充记录

发布于 2024-09-06 06:49:43 字数 261 浏览 8 评论 0原文

我有一个简单的记录结构,由标题 (H) 和数据行列表 (D) 1:N 组成。所有标题行必须以数字开头。所有数据行都有一个前导空格。其间还可能存在一些必须忽略的空行 (E)。

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

我想创建一个记录列表:

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

使用列表理解。最好的方法是什么?

I have a simple record structure consisting of a header (H) and a list of the data lines (D) 1:N. All header lines must start with a digit. All data lines have a leading whitespace. There also might be some empty lines (E) in between that must be ignored.

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

I would like to create a list of records:

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

using list comprehension. Whats the best way to do it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

拔了角的鹿 2024-09-13 06:49:43

在这种情况下,您必须使用lists:foldl/3 而不是列表推导式。使用foldl/3,您可以通过整个列表L累积标题和数据的值。

You must use lists:foldl/3 instead of list comprehensions in this case. With foldl/3 you can accumulate values of header and data through whole list L.

寂寞花火° 2024-09-13 06:49:43

你应该这样做:

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.

...以及许多其他变体。

You should do something like this:

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.

Anyway I think that straightforward Erlang version doesn't seems too complicated and should be little bit faster.

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

Edit: If you have to add better row classification or parsing, adding new function is better because it improves readability.

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

You can use it like this:

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.

Tail recursive native Erlang solution:

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

I think that there is no reason use tail recursion from performance point of view:

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.

... and many many other variants.

深海蓝天 2024-09-13 06:49:43

我需要折叠标题下方的所有数据行 - 所以目前这是我所拥有的:

  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.

I needed to collapse all Data lines beneath the header - so for the moment here is what I have:

  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.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文