如何从Erlang / Elixir中的集合中选择随机元素?

时间:2018-12-10 17:20:21

标签: erlang elixir ets

我需要在ets集中跟踪大量的进程,然后随机选择单个进程。所以我创建了这样的集合:

:ets.new(:pid_lookup, [:set, :protected, :named_table])

然后为了论证,让我们将self()坚持1000次:

Enum.map 1..1000, fn x -> :ets.insert(:pid_lookup, {x, self()}) end

现在我需要随机选择一个。我知道我可以使用:ets.lookup(:pid_lookup, :rand.uniform(1000))随机选择一个,但是如果我事先不知道集合的大小(在上述情况下为1000)怎么办?

如何找出一个ets集的大小?和/或有没有更好的方法从ets数据结构中选择随机pid?

2 个答案:

答案 0 :(得分:1)

  • 如果键是序列号
tab = :ets.new(:tab, [])
Enum.each(1..1000, & :ets.insert(tab, {&1, :value}))
size = :ets.info(tab,  :size) 
# size = 1000
value_picked_randomly = :ets.lookup(tab, Enum.random(1..1000)) 

:ets.info(tab, :size)返回表的大小;这是在给定表上插入的许多记录。

  • 如果您不知道按键是
first = :ets.first(tab)
:ets.lookup(tab, first)
func = fn key->
    if function_that_may_return_true() do
        key = case :ets.next(tab, key) do
         :'$end_of_table' -> throw :reached_end_of_table
         key -> func.(key)
        end
    else 
        :ets.lookup(tab, key)
    end
end
func.()

func将遍历ets表并返回一个随机值。 这将很耗时,因此对于具有大量记录的表而言,它并不是理想的解决方案。

答案 1 :(得分:-1)

根据我的评论,这是XY Problem

您真正需要的是跟踪更改列表并随机选择其元素之一。一般而言,ETS,尤其是:ets.set绝不打算查询大小。它们有不同的用途。

在您的监督树中生成Agent,保留已启动服务器的PID列表,并使用Kernel.length/1查询其大小,如果列表不是<,则甚至使用Enum.random/1 em>真正巨大(后者遍历整个枚举以获取随机元素。)