Erlang的split_binary函数的实现
Implementation of split_binary function of Erlang
我是 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.
如何改进我的代码?
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.
根据 split_binary/2 的 erlang 文档,这两个参数是一个 binary
,它不是一个列表,以及 bytes
的数量,你想在其中拆分二进制文件.
首先,您需要对什么是二进制文件有一个基本的了解。二进制是一个字节序列,其中每个字节都是 8 位,代表某个整数,例如
0010 0001
即 33
。这是一个二进制文件的例子:
<<1, 2, 3>>
当您没有为每个整数指定大小时,默认情况下每个整数将占用一个字节。如果你想让 2
占用两个字节,即 0000 0000 0000 0010
,即 16 位,那么你可以这样写:
<<1, 2:16, 3>>
shell 将显示为:
<<1,0,2,3>>
嗯?那个0是从哪里来的? shell逐字节显示二进制,整数0000 0000 0000 0010
的第一个字节是0000 0000
,也就是0。
接下来,您可以像处理列表一样单步执行二进制文件,一次从二进制文件的前面提取任意数量的位。碰巧 split_binary/2
从二进制文件的头部一次提取 8 位,或 1 个字节。
学习如何单步执行二进制文件有几个技巧:
对于列表,[]
表示空列表,对于二进制文件,<<>>
表示空二进制文件。
对于列表,你写 [Head|Tail]
来提取列表的头部,对于二进制文件,你写 <<Bits:3, Rest/binary>>
从二进制文件的前面提取 3 位。在您的情况下,您需要从二进制文件的前面提取 8 位。
这是您可以执行的操作的示例:
-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>>).
在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>>]
请注意,在构造二进制文件时,二进制文件的一个片段可以是另一个二进制文件:
23> Bin = <<1, 2, 3>>.
<<1,2,3>>
24> Acc = <<Bin/binary, 4>>.
<<1,2,3,4>>
如果你真的想实现lists:split/2,你可以这样做:
-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]).
在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]]
我认为@7stud 的回答是最好的,但我想添加一些关于你的代码的小细节,而不是实际检查它是否有效......
list:lenght/1
不存在(除非您还创建了自己的 list
模块。
- 如果您创建了自己的
list
模块,则不能在守卫中使用它。那里只允许 BIF。
- 如果您尝试使用
stdlib
的函数来检查列表的长度,那么您应该使用 erlang:length/1
或只使用 length/1
.
在 Erlang 中使用 snake_case
(例如 split_helper
)而不是 camelCase
(例如 splitHelper
) 一般用于模块名称、函数名称和原子。
您可以直接使用 pattern-matching 而不是编写 if
作为函数的唯一表达式...
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]).
元组用大括号而不是圆括号表示:{list:reverse([H|T]),…
。顺便说一句……这应该会阻止您的代码进行编译。错误应该看起来像 syntax error before: ','
此外,您可能已经编写了自己的 list
模块,但如果没有,并且您尝试使用 stdlib
功能,则 lists:reverse/1
不是list:reverse/1
.
最后,除了该列表之外,我强烈建议您为您的代码编写一些简单的测试。 This article 可能会帮助你。
我是 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.
如何改进我的代码?
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.
根据 split_binary/2 的 erlang 文档,这两个参数是一个 binary
,它不是一个列表,以及 bytes
的数量,你想在其中拆分二进制文件.
首先,您需要对什么是二进制文件有一个基本的了解。二进制是一个字节序列,其中每个字节都是 8 位,代表某个整数,例如
0010 0001
即 33
。这是一个二进制文件的例子:
<<1, 2, 3>>
当您没有为每个整数指定大小时,默认情况下每个整数将占用一个字节。如果你想让 2
占用两个字节,即 0000 0000 0000 0010
,即 16 位,那么你可以这样写:
<<1, 2:16, 3>>
shell 将显示为:
<<1,0,2,3>>
嗯?那个0是从哪里来的? shell逐字节显示二进制,整数0000 0000 0000 0010
的第一个字节是0000 0000
,也就是0。
接下来,您可以像处理列表一样单步执行二进制文件,一次从二进制文件的前面提取任意数量的位。碰巧 split_binary/2
从二进制文件的头部一次提取 8 位,或 1 个字节。
学习如何单步执行二进制文件有几个技巧:
对于列表,
[]
表示空列表,对于二进制文件,<<>>
表示空二进制文件。对于列表,你写
[Head|Tail]
来提取列表的头部,对于二进制文件,你写<<Bits:3, Rest/binary>>
从二进制文件的前面提取 3 位。在您的情况下,您需要从二进制文件的前面提取 8 位。
这是您可以执行的操作的示例:
-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>>).
在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>>]
请注意,在构造二进制文件时,二进制文件的一个片段可以是另一个二进制文件:
23> Bin = <<1, 2, 3>>.
<<1,2,3>>
24> Acc = <<Bin/binary, 4>>.
<<1,2,3,4>>
如果你真的想实现lists:split/2,你可以这样做:
-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]).
在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]]
我认为@7stud 的回答是最好的,但我想添加一些关于你的代码的小细节,而不是实际检查它是否有效......
list:lenght/1
不存在(除非您还创建了自己的list
模块。- 如果您创建了自己的
list
模块,则不能在守卫中使用它。那里只允许 BIF。 - 如果您尝试使用
stdlib
的函数来检查列表的长度,那么您应该使用erlang:length/1
或只使用length/1
.
- 如果您创建了自己的
在 Erlang 中使用
snake_case
(例如split_helper
)而不是camelCase
(例如splitHelper
) 一般用于模块名称、函数名称和原子。您可以直接使用 pattern-matching 而不是编写
if
作为函数的唯一表达式...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]).
元组用大括号而不是圆括号表示:
{list:reverse([H|T]),…
。顺便说一句……这应该会阻止您的代码进行编译。错误应该看起来像syntax error before: ','
此外,您可能已经编写了自己的
list
模块,但如果没有,并且您尝试使用stdlib
功能,则lists:reverse/1
不是list:reverse/1
.
最后,除了该列表之外,我强烈建议您为您的代码编写一些简单的测试。 This article 可能会帮助你。