一点都不熟悉Erlang,但是我想解释一下这段代码的用途?
以下是我对代码的理解。任何帮助都会有用。 我正在查看教程,但在这种情况下传递的值很混乱。
example- convert_list_to_k([{Name, {l, Weight}} | Rest]) //{1,Weight} <- This one
convert_list_to_k
中的值是如何返回的?
让我们说这个功能块
convert_list_to_k([{Name, {l, Weight}} | Rest]) ->
Converted_Object = {Name, {k, Weight / 0.45359237}},
[Converted_Object | convert_list_to_k(Rest)];
convert_list_to_k([Object | Rest]) ->
[Object | convert_list_to_k(Rest)];
convert_list_to_k([]) ->
[].
-module(erlang_program).
-export([format_weight/1]).
在上面的导出中,/ 1表示它将收到一个属性(我不知道哪个属性)
format_weight(List_of_objects) ->
Converted_List = convert_list_to_k(List_of_objects),
print_weight(Converted_List),
{Max_object, Min_object} = find_max_and_min(Converted_List),
print_max_and_min(Max_object, Min_object).
一种主要功能,它将导入convert_list_to_k
,print_weight(Converted_List)
,find_max_and_min(Converted_List)
和print_max_and_min(Max_object, Min_object).
根据我的理解,它做了以下事情:
我对传递[{Name, {l, Weight}} | Rest]
的方式感到困惑
convert_list_to_k([{Name, {l, Weight}} | Rest]) ->
Converted_Object = {Name, {k, Weight / 0.45359237}},
[Converted_Object | convert_list_to_k(Rest)];
convert_list_to_k([Object | Rest]) ->
[Object | convert_list_to_k(Rest)];
convert_list_to_k([]) ->
[].
print_weight([{Name, {k, Weight}} | Rest]) ->
io:format("~-15w ~w c~n", [Name, Weight]),
print_weight(Rest);
print_weight([]) ->
ok.
find_max_and_min([Object | Rest]) ->
find_max_and_min(Rest, Object, Object).
find_max_and_min([{Name, {k, Weight}} | Rest],
{Max_Name, {k, Max_Weight}},
{Min_Name, {k, Min_Weight}}) ->
if
Weight > Max_Weight ->
Max_Object = {Name, {k, Weight}};
true ->
Max_Object = {Max_Name, {k, Max_Weight}}
end,
if
Weight < Min_Weight ->
Min_Object = {Name, {k, Weight}};
true ->
Min_Object = {Min_Name, {k, Min_Weight}}
end,
find_max_and_min(Rest, Max_Object, Min_Object);
find_max_and_min([], Max_Object, Min_Object) ->
{Max_Object, Min_Object}.
print_max_and_min({Max_name, {k, Max_object}}, {Min_name, {k, Min_object}}) ->
io:format("Max weight was ~w c in ~w~n", [Max_object, Max_name]),
io:format("Min weight was ~w c in ~w~n", [Min_object, Min_name]).
答案 0 :(得分:1)
不要担心这段代码有点令人困惑。这有点不合时宜。我们马上解决这个问题......
在样式之前,请查看第一个函数convert_list_to_k/1
。它会有选择地将对象从标有l
的表单转换为标有k
的表单。
如何选择?它是作为参数传递给它的列表的第一个元素的形状和值上的匹配。如果它在l
内收到{Name, {l, Weight}}
类型值的值,则选择并运行第一个子句,将{l, Weight}
部分转换为{k, Weight}
值 - I假设这是&#34; l&#34;因为&#34;磅&#34;和&#34; k&#34;为&#34;千克&#34;。
此函数正在进行深度递归,这通常不适合这种特殊情况,因为Erlang(和大多数函数式语言)都具有尾递归的优化。
foo([Thing | Things]) ->
NewThing = change(Thing),
[NewThing | foo(Things)];
foo([]) ->
[].
这基本上就是这个功能正在做的事情。这意味着无论列表的大小如何,都必须添加一个新的调用堆栈层,因为如果没有记住每个中间值,就不能返回第一个子句中的原始列表。这对于没有大量内存开销的任意长列表不起作用,通常不是事情的工作方式。
想象一下,在记忆中看到这个:
foo([change(Thing1) | foo([change(Thing2) | foo([change(Thing3) | ...]]])
不是很整洁。 有时这是正确的事情,但不是在迭代列表的一般情况下。
尾递归版本如下所示:
foo(Things) ->
foo(Things, []).
foo([Thing | Things], Accumulator) ->
NewThing = change(Thing),
foo(Things, [NewThing | Accumulator]);
foo([], Accumulator) ->
lists:reverse(Accumulator).
此版本在常量空间中运行,是更明确递归的惯用形式。
那么所有匹配的东西呢?好吧,让我们说我想每次打印一公斤的价值,但我的一些价值是以磅为单位,有些是以公斤为单位。我可以将原始数值包装在元组中并使用原子来标记值,这样我就知道它们的意思了。例如,像{pounds, X}
这样的元组意味着我有一个数字X,它是以磅为单位,或者是一个元组{kilos, X}
,这意味着X是千克。两者都还很重。
那我的功能怎么样?
print_weight({kilos, X}) ->
io:format("Weight is ~wkgs~n", [X]);
print_weight({pounds, X}) ->
Kilos = X / 0.45359237,
io:format("Weight is ~wkgs~n", [Kilos]).
所以只要传递任何一种元组,这个函数都可以正常工作。
这些列表怎么样?我们可以像上面那样进行显式递归:
print_weights([{kilos, X} | Rest]) ->
ok = io:format("Weight is ~wkgs~n", [X]),
print_weights(Rest);
print_weight([{pounds, X} | Rest]) ->
Kilos = X / 0.45359237,
ok = io:format("Weight is ~wkgs~n", [Kilos]),
print_weights(Rest);
print_weights([]) ->
ok.
因此,它处理上面的值列表。但是我们真的不需要写下这些,是吗?我们已经有一个名为print_weight/1
的函数,它已经知道如何进行匹配。我们可以做的更简单地将print_weights/1
定义为使用列表操作的函数:
print_weights(List) ->
lists:foreach(fun print_weight/1, List).
请注意,当我们可以提供帮助时,我们通常不会进行明确的递归。原因是在简单的情况下我们已经有higher-order functions made to simplify simple iteration over lists。如果我们想要副作用并且不关心返回值,比如如上所示打印权重,我们会使用lists:foreach/2
。
回到&#34;改变&#34;上面的例子,如果我们已经知道我们想对每个值执行change/1
,但是返回完整的相同地图,那么使用list comprehension或lists:map/2
更有意义。< / p>
列表理解是地图上的特殊语法,也可以包括警卫。将函数映射到列表中的每个值并返回该列表的简单情况如下所示:
ChangedThings = [change(Thing) || Thing <- Things]
地图看起来几乎与lists:foreach/2
上面的方式完全相同:
ChangedThings = lists:map(fun change/1, Things)
现在,回到原来的例子......也许我们希望确保特定的值类型。所以我们可以编写一个只执行以下操作的简单函数:
ensure_metric({Name, {l, Pounds}}) ->
Kilos = Pounds / 0.45359237,
{Name, {k, Kilos}};
ensure_metric(Value = {_, {k, _}}) ->
Value.
这就是我们所需要的一切。上面发生的是{Foo, {l, Bar}}
形式的任何元组都与第一个子句匹配,并由该子句中的操作转换,然后重新打包为{Foo, {k, Baz}
形式,以及{{1}形式的任何元组匹配第二个但传递而不被更改。我们现在可以简单地将该函数映射到列表中:
{Foo, {k, Bar}}
一次只能简单地推理一个函数!
最小/最大功能有点疯狂。除非我们有完全有界的数学案例,否则我们不想写convert_list_to_k(List) ->
lists:map(fun ensure_metric/1, List).
。例如:
if
这是单个子句中的四个测试。 偶尔使用if
X > Y -> option1();
X =:= Y -> option2();
X == Y -> option3();
X < Y -> option4()
end,
对此有意义。但是,更常见的是,你最终得到了上面的内容,进行了简单的比较。在这种情况下,if
更具表现力:
case
BUT!也许我们在min / max函数中真正想要的只是操作守卫并避免编写一些复杂的主体逻辑。这是一个操作简单的数字列表的一个,稍微的改变将使它适合你正在处理的数据类型(那些元组):
case X > Y ->
true -> do_something();
false -> something_else()
end,
这里的程序逻辑不需要大量的猎豹翻转。
Erlang是如此简单而且很简单,作为一种语言,一旦大多数程序逻辑的不必要性突然沉入你的眼中,就会睁开新的眼睛&#34;。一些与背景信息相关的问答可能对您的旅程有所帮助: