我发现自己处于需要增加深度嵌套在一系列erlang记录中的值的位置。我对列表推导这样做的第一次尝试是令人沮丧的失败。最初,该列表包含许多记录,其中目标值将不存在,因为包含它的记录在某种程度上是未定义的。
我通过使用list来解决这个问题:分区只过滤掉那些实际需要递增的条目,但是我仍然无法提出一个能够进行这么简单操作的列表理解。
下面的代码示例可能无法编译 - 它只是为了演示我想要完成的任务。我把“未定义的案例(等等)”部分用来说明我原来的问题:
-record(l3, {key, value}).
-record(l2, {foo, bar, a_thing_of_type_l3}).
-record(l1, {foo, bar, a_thing_of_type_l2}).
increment_values_recursive([], Acc
increment_values_recursive([L1 | L1s], Acc) ->
case L1#l1.a_thing_of_type_l2 of
undefined -> NewRecord = L1;
L2 ->
case L2#l2.a_thing_of_type_l3 of
undefined -> NewRecord = L2;
{Key, Value} ->
NewRecord = L1#l1{l2 = L2#l2{l3 = {Key, Value + 1}}}
end
end,
increment_values_recursive(L1s, [NewRecord | Acc]).
increment_values(L1s) ->
lists:reverse(increment_values_recursive(L1s, [])).
........
NewList = increment_values(OldList).
这就是我的开始,但我很高兴看到一个列表理解,当列表不必检查未定义的成员时,它将处理这个。这样的事情,真的:
increment_values_recursive([], Acc
increment_values_recursive([L1 | L1s], Acc) ->
%I'm VERY SURE that this doesn't actually compile:
#l1{l2 = #l2{l3 = #l3{_Key, Value} = L3} = L2} = L1,
%same here:
NewRecord = L1#l1{l2=L2#l2{l3=L3#l3{value = Value+1}}},
increment_values_recursive(L1s, [NewRecord | Acc]).
increment_values(L1s) ->
lists:reverse(increment_values_recursive(L1s, [])).
AKA:
typedef struct { int key, value; } l3;
typedef struct { int foo, bar; l3 m_l3 } l2;
typedef struct { int foo, bar; l2 m_l2 } l1;
for (int i=0; i<NUM_IN_LIST; i++)
{
objs[i].m_l2.m_l3.value++;
}
答案 0 :(得分:3)
您可以使用列表推导,甚至不需要过滤掉没有嵌套的记录。
为避免可读性问题,我缩短了您的记录定义。
-record(l3, {key, value}).
-record(l2, {foo, bar, al3}).
-record(l1, {foo, bar, al2}).
定义辅助函数以递增值:
inc_value(#l1{al2=#l2{al3=#l3{value=Value}=L3}=L2}=L1) ->
L1#l1{al2=L2#l2{al3=L3#l3{value=Value+1}}};
inc_value(R) ->
R.
请注意最后一个子句,它将与模式不匹配的任何其他内容映射到自身。
让我们定义示例记录来试试这个:
1> R=#l1{foo=1, bar=2}.
#l1{foo = 1,bar = 2,al2 = undefined}
这是一个没有定义完整嵌套的记录。
2> R1=#l1{foo=1, bar=2, al2=#l2{foo=3, bar=4, al3=#l3{key=mykey, value=10}}}.
#l1{foo = 1,bar = 2,
al2 = #l2{foo = 3,bar = 4,
al3 = #l3{key = mykey,value = 10}}}
另一个具有完整结构的人。
尝试帮助函数:
4> inc_value(R).
#l1{foo = 1,bar = 2,al2 = undefined}
单独留下未完全嵌套的记录。
3> inc_value(R1).
#l1{foo = 1,bar = 2,
al2 = #l2{foo = 3,bar = 4,
al3 = #l3{key = mykey,value = 11}}}
它会使完全嵌套的记录增加。
现在列表理解简单易读:
5> [ inc_value(X) || X <- [R, R1] ].
[#l1{foo = 1,bar = 2,al2 = undefined},
#l1{foo = 1,bar = 2,
al2 = #l2{foo = 3,bar = 4,
al3 = #l3{key = mykey,value = 11}}}]
答案 1 :(得分:2)
这是 waaaay 比使用具有破坏性突变的语言更加混乱,但绝对可能。这是污垢:
increment(Records) ->
[L1#l1{l2 = (L1#l1.l2)#l2{l3 = ((L1#l1.l2)#l2.l3)#l3{value = ((L1#l1.l2)#l2.l3)#l3.value + 1}}} || L1 <- Records].
正如你所看到的,这是丑陋的地狱;此外,很难立即理解这种理解是做什么的。直截了当地弄清楚发生了什么,但我和我店里的任何人都有过这样的话题。简单地累积和反转要好得多 - Erlang编译器和运行时非常适合优化这种模式。
答案 2 :(得分:1)
它并不像看起来那么难。 @Peer Stritzinger给出了一个很好的答案,但这是我的看法,清晰的列表理解:
-record(l3, {key, value}).
-record(l2, {foo=foo, bar=bar, al3}).
-record(l1, {foo=foo, bar=bar, al2}).
increment(#l1{al2 = Al2}=L1) -> L1#l1{al2 = increment(Al2)};
increment(#l2{al3 = Al3}=L2) -> L2#l2{al3 = increment(Al3)};
increment(#l3{value = V}=L3) -> L3#l3{value = V + 1}.
test() ->
List =
[ #l1{al2=#l2{al3=#l3{key=0, value = 100}}}
, #l1{al2=#l2{al3=#l3{key=1, value = 200}}}
, #l1{al2=#l2{al3=#l3{key=2, value = 300}}}
, #l1{al2=#l2{al3=#l3{key=3, value = 400}}}],
[increment(L) || L <- List].
答案 3 :(得分:0)
最好的解决方案可能是在函数式编程中研究镜头的概念。 镜头是用于记录变异的功能性getter和setter。如果操作正确,您可以编写高阶镜头撰写原始镜头。
结果是你可以为你的目的构造一个mutator,然后通过理解来运行mutator遍历所有记录。
这是我想有一天为Erlang写的东西之一,但从来没有真正有时间写下来:)