Erlang的split_binary函数的实现

b0zn9rqh  于 2022-12-08  发布在  Erlang
关注(0)|答案(2)|浏览(148)

我是Erlang领域的新手,我正在尝试实现split_binary函数,该函数将(list,index)作为输入,并根据索引将列表拆分为两个列表。

split(Lst, N) when N>=list:lenght(Lst) -> Lst;
split(Lst, N) when N<list:lenght(Lst) -> splitHelper(list:reverse(Lst), 0, N, []).

splitHelper([H|T], X, N, Acc) ->
  if
    X>=N ->
      (list:reverse([H|T]), list:reverse(Acc));
    X<N ->
      splitHelper(T, X+1, N, [H|Acc])
  end.

如何改进我的代码?

cidc1ykv

cidc1ykv1#

I'm new in the Erlang world. I'm trying to implement the function split_binary. The function takes as input (list, index) and it splits the list in two lists according to the index.
According to the erlang docs for split_binary/2, the two arguments are a binary , which is not a list, and the number of bytes where you want to split the binary.
First, you need to have a basic understanding of what a binary is. A binary is a sequence of bytes, where each byte is 8 bits representing some integer, e.g.
0010 0001
which is 33 . Here is an example of a binary:

<<1, 2, 3>>

When you don't specify a size for each integer, by default each integer will occupy one byte. If you wanted the 2 to occupy two bytes instead, i.e. 0000 0000 0000 0010 , which is 16 bits, then you could write:

<<1, 2:16, 3>>

which the shell would display as:

<<1,0,2,3>>

Huh? Where did that 0 come from? The shell displays a binary byte by byte, and the first byte of the integer 0000 0000 0000 0010 is 0000 0000 , which is 0.
Next, you can step through a binary just like you can for a list, extracting any number of bits at a time from the front of the binary. It so happens that split_binary/2 extracts 8 bits, or 1 byte, at a time from the head of the binary.
There are a couple of tricks to learning how to step through a binary:

  1. For lists, [] means an empty list, and for binaries <<>> means an empty binary.
  2. For lists you write [Head|Tail] to extract the head of the list, and for binaries you write <<Bits:3, Rest/binary>> to extract 3 bits from the front of the binary. In your case, you need to extract 8 bits from the front of the binary.
    Here is an example of what you can do:
-module(a).
-compile(export_all).

split_b(Bin, N) ->
    split_b(Bin, N, _Acc = <<>>).

split_b(     Bin,               _N = 0, Acc) -> [Acc, Bin];
split_b(<<Bits:8, Rest/binary>>, N,     Acc) ->
    split_b(Rest, N-1, <<Acc/binary, Bits>>).

In the shell:

40> c(a).
a.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,a}

41> a:split_b(<<5,6,7>>, 1).
[<<5>>,<<6,7>>]

42> a:split_b(<<5,6,7>>, 2).
[<<5,6>>,<<7>>]

Note that when constructing a binary one of the segments of the binary can be another binary:

23> Bin = <<1, 2, 3>>.        
<<1,2,3>>

24> Acc = <<Bin/binary, 4>>.
<<1,2,3,4>>

If you are actually trying to implement lists:split/2, you can do this:

-module(a).
-compile(export_all).

split_l(N, List) -> 
    split_l(N, List, _Acc=[]).

split_l(_N=0, List, Acc) ->
    [lists:reverse(Acc), List];
split_l(N, [H|T], Acc) -> 
    split_l(N-1, T, [H|Acc]).

In the shell:

2> c(a).
a.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,a}

3> a:split_l(1, [10, 20, 30]).
["\n",[20,30]]

4> shell:strings(false).
true

5> a:split_l(1, [10, 20, 30]).
[[10],[20,30]]

6> a:split_l(2, [10, 20, 30]).
[[10,20],[30]]
uubf1zoe

uubf1zoe2#

I think @7stud's answer is the best one, but I wanted to add a few minor details about your code, without actually checking if it works or not…

  • list:lenght/1 doesn't exist (unless you also created your own list module.
  • If you created your own list module, you can't use it in guards. Only BIFs are allowed there.
  • If you're trying to use stdlib 's function to check the length of a list, then you should use erlang:length/1 or just length/1 .
  • It's more idiomatic in Erlang to use snake_case (e.g. split_helper ) instead of camelCase (e.g. splitHelper ) for module names, function names and atoms in general.
  • You can use pattern-matching directly instead of writing an if as the sole expression of your function…
split_helper([H|T], X, N, Acc) when X > N ->
   (list:reverse([H|T]), list:reverse(Acc));
 split_helper([H|T], X, N, Acc) when X<N ->
   split_helper(T, X+1, N, [H|Acc]).
  • Tuples are denoted with curly braces and not parentheses: {list:reverse([H|T]),… . BTW… This should have prevented your code from compiling at all. The error should've looked like syntax error before: ','
  • Also, you might have written your own list module, but if not and if you're trying to use stdlib functionality, it's lists:reverse/1 not list:reverse/1 .

Finally, out of that list, I would strongly recommend you to write some simple tests for your code. This article may help you with that.

相关问题