Обновить значения списка - PullRequest
1 голос
/ 08 октября 2011

У меня есть следующие настройки:

1> rd(rec, {name, value}).
rec
2> L = [#rec{name = a, value = 1}, #rec{name = b, value = 2}, #rec{name = c, value = 3}].
[#rec{name = a,value = 1},
 #rec{name = b,value = 2},
 #rec{name = c,value = 3}]
3> M = [#rec{name = a, value = 111}, #rec{name = c, value = 333}].
[#rec{name = a,value = 111},#rec{name = c,value = 333}]

Элементы в списке L уникальны в зависимости от их name.Я также не знаю предыдущих значений элементов в списке M.Я пытаюсь обновить список L значениями в списке M, сохраняя при этом элементы L, которых нет в M.Я сделал следующее:

update_values([], _M, Acc) ->
    Acc;
update_attributes_from_fact([H|T], M, Acc) ->
    case [X#rec.value || X <- M, X#rec.name =:= H#rec.name] of
      [] ->
        update_values(T, M, [H|Acc]);
      [NewValue] ->
        update_values(T, M, [H#rec{value = NewValue}|Acc])
    end.

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

Большое спасибо.

Ответы [ 3 ]

3 голосов
/ 08 октября 2011

Нет существующей функции, которая делает это за вас, так как вы просто хотите обновить поле значения, а не заменять всю запись в L (как списки: keyreplace () делает).Если и L, и M могут быть длинными, я рекомендую изменить L из списка на dict или gb_tree, используя в качестве ключа # rec.name.Затем вы можете выполнить цикл по M, и для каждого элемента в M найдите правильную запись, если она есть, и запишите обновленную запись.Цикл может быть записан в виде сгиба.Даже если вы сначала преобразуете список L в диктовку, а после цикла конвертируете его обратно после цикла, это будет более эффективно, чем подход L * M.Но если M всегда короток, и вы не хотите, чтобы L оставалось в остальной части кода, то ваш текущий подход хорош.

1 голос
/ 10 октября 2011

Чистое решение для понимания списка:

[case [X||X=#rec{name=XN}<-M, XN=:=N] of [] -> Y; [#rec{value =V}|_] -> Y#rec{value=V} end || Y=#rec{name=N} <- L].

немного более эффективно, используя lists:keyfind/3:

[case lists:keyfind(N,#rec.name,M) of false -> Y; #rec{value=V} -> Y#rec{value=V} end || Y=#rec{name=N} <- L].

, еще более эффективно для больших M:

D = dict:from_list([{X#rec.name, X#rec.value} || X<-M]),
[case dict:find(N,D) of error -> Y; {ok,V} -> Y#rec{value=V} end || Y=#rec{name=N} <- L].

но для действительно большого M этот подход может быть самым быстрым:

merge_join(lists:keysort(#rec.name, L), lists:ukeysort(#rec.name, M)).

merge_join(L, []) -> L;
merge_join([], _) -> [];
merge_join([#rec{name=N}=Y|L], [#rec{name=N, value=V}|_]=M) -> [Y#rec{value=V}|merge_join(L,M)];
merge_join([#rec{name=NL}=Y|L], [#rec{name=NM}|_]=M) when NL<NM -> [Y|merge_join(L,M)];
merge_join(L, [_|M]) -> merge_join(L, M).
0 голосов
/ 08 октября 2011

Вы можете использовать списки: ukeymerge / 3 :

lists:ukeymerge(#rec.name, M, L).

Что:

возвращает отсортированный список, образованный слиянием TupleList1 и TupleList2. Объединение выполняется для N-го элемента каждого кортежа. И то и другое TupleList1 и TupleList2 должны быть отсортированы по ключам без дубликатов до для оценки этой функции. Когда два кортежа сравниваются равными, кортеж из TupleList1 выбран, а один из TupleList2 удален.

Запись является кортежем, и вы можете использовать # rec.name для прозрачного возврата позиции ключа. Обратите внимание, что я перевернул списки L и M, поскольку функция сохраняет значение из первого списка.

...