在Erlang中展平元组

时间:2016-01-10 00:14:46

标签: erlang

我试图改变形式的元组:

{{A,B,{C,A,{neg,A}}},{A,B,{neg,A}}}

{{A,B,C,A,{neg,A}},{A,B,{neg,A}}

我对Erlang很新,所以我很感激任何提示。如果最终结构是列表或元组,只要任何以neg开头的字母保留为元组/列表,就没有区别。

1 个答案:

答案 0 :(得分:1)

一个简单的解决方案:

convert({{A,B,{C,D,E}},F}) -> {{A,B,C,D,E},F}.

如果这有效的原因令人费解,请考虑:

1> YourTuple = {{a, b, {c, a, {neg, a}}}, {a, b, {neg, a}}}.
{{a,b,{c,a,{neg,a}}},{a,b,{neg,a}}}
2> Convert = fun({{A,B,{C,D,E}},F}) -> {{A,B,C,D,E},F} end.
#Fun<erl_eval.6.54118792>
3> Convert(YourTuple).
{{a,b,c,a,{neg,a}},{a,b,{neg,a}}}

发生这种情况的原因是因为我们基于数据的形状 匹配整个值。这就是匹配的重点,也是为什么它在很多情况下都非常有用(以及为什么我们想在一种语言中使用元组来匹配VS的语言,其中“一切都是可迭代的”)。我们可以用任何替换细节,它们将被匹配并相应地返回:

4> MyTuple = {{"foo", bar, {<<"baz">>, balls, {ugh, "HURR!"}}}, {"Fee", "fi", "fo", "fum"}}.
{{"foo",bar,{<<"baz">>,balls,{ugh,"HURR!"}}},
 {"Fee","fi","fo","fum"}}
5> Convert(MyTuple).
{{"foo",bar,<<"baz">>,balls,{ugh,"HURR!"}},
 {"Fee","fi","fo","fum"}}

为什么当顶级对的最后一个元素的形状与第一个元素的形状不同时,这是否有效?因为关于第二个元素的所有被绑定到F所代表的函数中的符号Convert(请注意,在shell中我为了方便而命名了一个匿名函数,这将是完全与使用我在本答案顶部写的convert/1相同。我们不关心第二个元素是什么 - 实际上我们不想关心的细节。有选择地不关心给定数据元素形状的自由是我们在Erlang中使用的关键抽象之一。

“但那些只是原子'a''b''c'等等。我在那里有不同的东西!”

只是为了使它看起来像你上面的例子(并强化我所说的不关心我们绑定给定变量的确切内容):

6> A = 1.
1
7> B = 2.
2
8> C = 3.
3
9> AnotherTuple = {{A, B, {C, A, {neg, A}}}, {A, B, {neg, A}}}.
{{1,2,{3,1,{neg,1}}},{1,2,{neg,1}}}
10> Convert(AnotherTuple).
{{1,2,3,1,{neg,1}},{1,2,{neg,1}}}

但是,要做到这一点通常不是最佳选择。一般来说,首先生成该数据的程序的其他部分应该为您返回有用的数据类型。如果没有,你当然可以将它们隐藏在转换函数之后(例如上面的转换函数)(特别是当你处理不受你控制的API时),但一般来说,这需要代码味道。

继续前进

“需要扁平元组”的更一般情况有点不同。

元组是元组,因为它中的每个位置都有含义。因此,您通常不会听到需要“扁平化元组”的人,因为这会从根本上改变您正在处理的数据的含义。如果您遇到此问题,则不应该使用元组开头。

也就是说,我们可以将元组转换为列表,我们可以检查数据元素的形状。有了这两个操作,我们就可以编写一个程序来移动一个tuplish结构,从里面发现的任何内容构建一个列表。一个天真的实现可能如下所示:

-module(tuplish).
-export([flatten/1]).

-spec flatten(list() | tuple()) -> list().
flatten(Thing) ->
    lists:flatten(flatten(Thing, [])).

flatten(Thing, A) when is_tuple(Thing) ->
    flatten(tuple_to_list(Thing), A);
flatten([], A) ->
    lists:reverse(A);
flatten([H | T], A) when is_tuple(H) ->
    flatten(T, [flatten(H) | A]);
flatten([H | T], A) when is_list(H) ->
    flatten(T, [flatten(H) | A]);
flatten([H | T], A) ->
    flatten(T, [H | A]).

请记住,在编写Erlang代码几年后,我从不需要实际执行此操作。请记住:元组意味着与列表不同的东西。

所有这一切,使用records几乎可以肯定你所遇到的问题。