erlang Elixir :更新元素中一组值的最简洁方法是什么?

s8vozzvw  于 2022-12-08  发布在  Erlang
关注(0)|答案(1)|浏览(151)

问题

在学习使用Mnesia with Elixir时,我创建了一个具有多个功能(如读、写...)的表,其中一个功能是更新一组字段(大小从1到count - 1),而不更改其余数据,并将逻辑限制在Mnesia事务内部的最小值。
在我的搜索过程中,我碰巧发现了这个:Erlang : Mnesia : Updating a single field value in a row(下面的代码)。这是相同的问题,但对Erlang,而不是对Elixir。

代码

据我所知,它在Erlang中工作,因为read返回一个元组,该元组直接设置在一个记录中,这允许我们在write操作中保存指定的数据。

update_a(Tab, Key, Value) ->
  fun() ->
    [P] = mnesia:wread({Tab, Key}),
    mnesia:write(P#pixel{a=Value})
  end.

这里,对于Elixir,即使记录存在,它也只是一个元组,你只能改变索引中的数据,并将完整的元组返回给写操作。

Table: {table_name, id, data1, data2, data3, data4}
changes = [{2, new_val_for_data1}, {4, new_val_for_data3}]

def handle_call({:update_and_read, table, {id, changes}}, _, state) do
  {:atomic, new_object} =
    :mnesia.transaction(fn ->
      object =
        :mnesia.wread({table, id})
        |> List.first()

      ret =
        Enum.reduce(changes, object, fn {index, value}, acc ->
          acc |> Tuple.delete_at(index) |> Tuple.insert_at(index, value)
        end)

      :mnesia.write(object)
      ret
    end)

  {:reply, {:ok, new_object}, state}
end

问题

在Elixir中有没有可能有更短的功能(理想情况下,像Erlang中的2行)?

mzaanser

mzaanser1#

您可以使用Record执行一些预备步骤(例如在应用程序中定义记录)。

defmodule Pixel do
  require Record
  Record.defrecord(:table_name, id: nil, data1: nil, data2: nil)
end

以及要更新表的模块中

import Pixel

def handle_call({:update_and_read, table, {id, changes}}, _, state) do
  # changes = [data2: new_val_for_data1, ...]
  {:atomic, result} =
    :mnesia.transaction(fn ->
      case :mnesia.wread({table, id}) do
        [object] -> 
          :mnesia.write(pixel(object, changes))
          {:ok, object}
        [] -> :error
      end
    end)

  {:reply, result, state}
end

另一种可能性是

changes = %{2 => new_val_for_data1, 4 => new_val_for_data3}
object
|> Tuple.to_list()
|> Enum.with_index()
|> Enum.map(fn {value, idx} -> Map.get(changes, idx, value) end)
|> List.to_tuple()

另一种可能性是声明一个宏,该宏接受一个元组(表示一个表行)和一个{idx, new_value}元组列表,并就地更改相应的元素。

相关问题