在 erlang 中,如何“列表理解”?深度嵌套记录的操作?
我发现自己需要增加一个深深嵌套在一系列 erlang 记录中的值。我第一次尝试使用列表推导式来做到这一点是令人沮丧的失败。最初,该列表包含许多记录,其中目标值不存在,因为包含目标值的记录在某种程度上是未定义的。
我通过使用lists:partition来过滤掉那些实际需要递增的条目,很容易地处理这个问题,但我仍然无法想出一个可以执行如此简单操作的列表理解。
下面的代码示例可能无法编译 - 它只是为了演示我试图完成的任务。我用“未定义的情况(废话)”部分来说明我原来的问题:
-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, [])).
又名:
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++;
}
I found myself in the position of needing to increment a value which was deeply nested in a series of erlang records. My first attempts at doing this with list comprehensions were dismal failures. Originally, the list contained a number of records where the target value would be absent because the record that contained it would, at some level, be undefined.
I dealt with that easily enough by using lists:partition to filter out only those entries that actually needed incrementing, but I was still unable to come up with a list comprehension that would do such a simple operation.
The code sample below probably doesn't compile - it is simply to demonstrate what I was trying to accomplish. I put the "case (blah) of undefined" sections to illustrate my original problem:
-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).
That was what I started with, but I'd be happy to see a list comprehension that would process this when the list didn't have to check for undefined members. Something like this, really:
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++;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您可以使用列表理解,甚至不需要过滤掉没有嵌套的记录。
为了避免可读性问题,我缩短了您的记录定义。
定义一个辅助函数来增加值:
请注意最后一个子句,它将与模式不匹配的任何其他内容映射到自身。
让我们定义示例记录来尝试一下:
这是一条没有定义完整嵌套的记录。
另一种具有完整结构。
尝试一下辅助函数:
它会保留未完全嵌套的记录。
它可以增加完全嵌套的记录。
现在列表理解简单易读:
You can use a list comprehension and even don't need to filter out records that don't have the nesting.
To avoid readability problems I shortened your record definition.
Define a helper function to increment the value:
Note the last clause that maps any other stuff that doesn't match the pattern to itself.
Lets define example records to try this out:
This is a record that doesn't have the full nesting defined.
Another one that has the full structure.
Try out the helper function:
It leaves alone the not fully nested record.
It increments the fully nested record ok.
Now the list comprehension is simple and readable:
这比具有破坏性突变的语言更加混乱,但这绝对是可能的。这是污垢:
正如你所看到的,这实在是太丑了;此外,很难立即理解这个理解在做什么。弄清楚发生了什么很简单,但我会和我店里写过这样的东西的任何人谈谈。简单地累加和反转要好得多——Erlang编译器和运行时非常擅长优化这种模式。
This is waaaay messier than it would be in a language with destructive mutation, but it is definitely possible. Here's the dirt:
As you can see, this is ugly as hell; furthermore, it's difficult to immediately apprehend what this comprehension is doing. It's straightforward to figure out what's going on, but I'd have a talk with anyone in my shop who wrote something like this. Much better to simply accumulate and reverse - the Erlang compiler and runtime are very good at optimizing this sort of pattern.
它并不像看起来那么难。 @Peer Stritzinger 给出了一个很好的答案,但这是我的看法,具有清晰的列表理解:
It is not as hard as it seems. @Peer Stritzinger gave a good answer, but here is my take, with a clean list comprehension:
最好的解决方案可能是研究函数式编程中透镜的概念。 lens 是一个用于记录突变的功能性 getter 和 setter。正确完成后,您就可以编写组成原始透镜的高阶透镜。
结果是,您可以根据您的目的构造一个变体,然后通过理解来运行变体通过所有记录。
这是我有一天想为 Erlang 写的事情之一,但一直没有时间写:)
The best solution is probably to look into the concept of lenses in functional programming. A lens is a functional getter and setter for mutation of records. Done correctly, you can then write higher-order lenses which compose primitive lenses.
The result is that you can construct a mutator for your purpose and then run the mutator through all the records by a comprehension.
It is one of those things I wanna write some day for Erlang but never really got the time to write up :)