В erlang, как вы «понимаете» список манипуляций с глубоко вложенными записями? - PullRequest
1 голос
/ 20 августа 2011

Я оказался в положении необходимости увеличивать значение, которое было глубоко вложено в серию записей erlang.Моими первыми попытками сделать это с помощью списочного понимания были неудачные неудачи.Первоначально список содержал ряд записей, в которых целевое значение отсутствовало бы, поскольку содержащая его запись на каком-то уровне была бы неопределенной.

Я достаточно легко справился с этим, используя списки: раздел для фильтрациитолько те записи, которые действительно нуждались в приращении, но я все еще не смог придумать понимание списка, которое бы выполняло такую ​​простую операцию.

Пример кода ниже, вероятно, не компилируется - это просто для демонстрациичто я пытался сделать.Я поместил разделы «case (blah) of undefined»), чтобы проиллюстрировать мою первоначальную проблему:

-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++;
}

Ответы [ 4 ]

3 голосов
/ 20 августа 2011

Вы можете использовать понимание списка, и даже не нужно отфильтровывать записи, которые не имеют вложенности.

Чтобы избежать проблем с читабельностью, я сократил определение вашей записи.

-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}}}

Увеличивает полностью вложенную запись ok.

Теперь списокпонимание простое и читаемое:

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}}}]
2 голосов
/ 20 августа 2011

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} *} *} * * * * Вот грязь:

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 и среда выполнения очень хороши для оптимизации такого рода паттернов.

1 голос
/ 31 августа 2011

Это не так сложно, как кажется.@Perer 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].
0 голосов
/ 20 августа 2011

Лучшим решением, вероятно, является изучение концепции линз в функциональном программировании. A lens - это функциональный геттер и сеттер для мутации записей. Если все сделано правильно, вы можете написать линзы высшего порядка, которые составляют примитивные линзы.

В результате вы можете создать мутатор для своей цели, а затем запустить мутатор по всем записям с помощью понимания.

Это одна из тех вещей, которые я когда-нибудь хочу написать для Эрланга, но у меня никогда не было времени, чтобы написать:)

...