Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/elixir/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Elixir 长生不老药:在列表地图中找到最小长度列表的键_Elixir - Fatal编程技术网

Elixir 长生不老药:在列表地图中找到最小长度列表的键

Elixir 长生不老药:在列表地图中找到最小长度列表的键,elixir,Elixir,在Elixir中的列表映射中,找到与最小长度列表相关联的键的有效方法是什么。假设我有: z = %{a: [1, 2, 3], b: [4, 5], c: [6, 7, 8, 9]} 我知道我能做到: Enum.map z, fn {k, v} -> length(v) end 这将给我: [3, 2, 4] 但我真正需要的只是答案,即与最小值2相关联的键,当然是:b 我将在列表的动态地图上大约每秒钟运行一次,所以我希望它尽可能高效 用长生不老药做任何事情最有效的方法是: Enum.

在Elixir中的列表映射中,找到与最小长度列表相关联的键的有效方法是什么。假设我有:

z = %{a: [1, 2, 3], b: [4, 5], c: [6, 7, 8, 9]}
我知道我能做到:

Enum.map z, fn {k, v} -> length(v) end
这将给我:

[3, 2, 4]
但我真正需要的只是答案,即与最小值2相关联的键,当然是:b


我将在列表的动态地图上大约每秒钟运行一次,所以我希望它尽可能高效

用长生不老药做任何事情最有效的方法是:

Enum.reduce(z,{nil,0},fn
{k,v},{u,len}当len==0或len>length(v)->{k,length(v)}
_,acc->acc
(完)
#⇒{:b,2}
要仅获取
:b
,模式匹配结果:

{key,{}=⇑以上⇑
或者(更糟糕的是)管道到
|>Tuple.to_list()|>list.first

在这里,我们更新累加器,如果:

  • 我们刚刚开始,保存的值为空,或者
  • 新的长度比旧的短

用长生不老药做任何事情最有效的方法是:

Enum.reduce(z,{nil,0},fn
{k,v},{u,len}当len==0或len>length(v)->{k,length(v)}
_,acc->acc
(完)
#⇒{:b,2}
要仅获取
:b
,模式匹配结果:

{key,{}=⇑以上⇑
或者(更糟糕的是)管道到
|>Tuple.to_list()|>list.first

在这里,我们更新累加器,如果:

  • 我们刚刚开始,保存的值为空,或者
  • 新的长度比旧的短

如果你在寻找速度,
:maps.fold
通常比Enum.reduce快一点点。这是一个比@mudasobwa的实现快10%的实现:

:maps.fold(
  fn k, v, {_, min} = acc ->
    l = length(v)
    if min == 0 || l < min, do: {k, l}, else: acc
  end,
  {nil, 0},
  map
)
|> elem(0)

如果你在寻找速度,
:maps.fold
通常比
Enum.reduce
快一点点。这是一个比@mudasobwa的实现快10%的实现:

:maps.fold(
  fn k, v, {_, min} = acc ->
    l = length(v)
    if min == 0 || l < min, do: {k, l}, else: acc
  end,
  {nil, 0},
  map
)
|> elem(0)

除非绝对必要,否则我更喜欢简单而不是性能,所以如果性能不太重要,我会这样做:

map
|> Enum.min_by(fn {_k, v} -> length(v) end)
|> elem(0)

通过@Dogbert添加到基准测试中可以发现,在我的机器上,平均90µs/次迭代的速度大约慢1.5倍。这仍然有足够的空间让它每秒钟运行一次。

除非绝对必要,否则我更喜欢简单而不是性能,所以如果性能不太重要,我会这样做:

map
|> Enum.min_by(fn {_k, v} -> length(v) end)
|> elem(0)

通过@Dogbert添加到基准测试中可以发现,在我的机器上,平均90µs/次迭代的速度大约慢1.5倍。这仍然有足够的空间让它每秒钟运行一次。

对于任何想重复@Dogbert的基准测试的人,包括Patrick Oscity的答案:

map = for(key <- 1..1000, into: %{}, do: {key, Enum.random([[1, 2, 3], [4, 5], [6, 7, 8, 9]])})

Benchee.run(%{
  "mudasobwa" => fn ->
    Enum.reduce(map, {nil, 0}, fn
      {k, v}, {_, len} when len == 0 or len > length(v) -> {k, length(v)}
      _, acc -> acc
    end)
    |> elem(0)
  end,
  "dogbert" => fn ->
    :maps.fold(
      fn k, v, {_, min} = acc ->
        l = length(v)
        if min == 0 || l < min, do: {k, l}, else: acc
      end,
      {nil, 0},
      map
    )
    |> elem(0)
    end,
  "oscity" => fn ->
        map
        |> Enum.min_by(fn {_k, v} -> length(v) end)
        |> elem(0)
    end
})
Benchee代码如下:

我只是加了一句
{:benchee,“~>0.12.1”}

在mix.exs中的deps下

对于希望重复@Dogbert基准的任何人,包括Patrick Oscity的答案:

map = for(key <- 1..1000, into: %{}, do: {key, Enum.random([[1, 2, 3], [4, 5], [6, 7, 8, 9]])})

Benchee.run(%{
  "mudasobwa" => fn ->
    Enum.reduce(map, {nil, 0}, fn
      {k, v}, {_, len} when len == 0 or len > length(v) -> {k, length(v)}
      _, acc -> acc
    end)
    |> elem(0)
  end,
  "dogbert" => fn ->
    :maps.fold(
      fn k, v, {_, min} = acc ->
        l = length(v)
        if min == 0 || l < min, do: {k, l}, else: acc
      end,
      {nil, 0},
      map
    )
    |> elem(0)
    end,
  "oscity" => fn ->
        map
        |> Enum.min_by(fn {_k, v} -> length(v) end)
        |> elem(0)
    end
})
Benchee代码如下:

我只是加了一句
{:benchee,“~>0.12.1”}


在mix.exs中的deps下

如果多个列表的最小值相等怎么办?@KevinJohnson那么我在最小值相等的列表之间矛盾,但我仍然只需要一个选择。如果多个列表的最小值相等怎么办?@KevinJohnson那么我在最小值相等的列表之间矛盾,但我仍然只需要一个选项。@ThomasBrowne
first
,但要避免使用它。按说明使用匹配。
|>Tuple.to_list()|>list.first()
只是
|>elem(0)
@ThomasBrowne
first
,但请避免使用它。按说明使用匹配。
|>Tuple.to_list()|>list.first()
只是
|>元素(0)
。另外,偏差25%建议不要将10%的差异看得太严重。您是如何进行实际基准测试的?“它是内置在iex中的吗?”我用过托马斯布朗。它不是内置的。@Dogbert不错的。现在有没有一种“快速而肮脏”的方法可以在不启动新项目并将其放入mix.exs的情况下将这个(或者通常是任何模块)导入到iex会话中?@ThomasBrowne不太可能。我要做的是:克隆benchee,
cd
到其中,将代码复制到
foo.exs
,然后运行
mix-run-foo.exs
。另外25%的偏差建议不要把10%的差异看得太严重。您是如何进行实际的基准测试的?“它是内置在iex中的吗?”我用过托马斯布朗。它不是内置的。@Dogbert不错的。现在有没有一种“快速而肮脏”的方法可以在不启动新项目并将其放入mix.exs的情况下将这个(或者通常是任何模块)导入到iex会话中?@ThomasBrowne不太可能。我所做的是:克隆benchee,
cd
到其中,将代码复制到
foo.exs
,然后运行
mix-run-foo.exs
。是的,我会在我的答案或@mudasobwa的答案上使用它,除非保存那些微秒是非常重要的。令人惊讶的是,我正在Arm架构的机器上运行它,这样可能会有所不同,但这至少比其他两个答案快2倍。因此,结合其美丽的简单性,您将获得胜利。(对于其他的答案,我非常感谢fold和reduce得到了很好的演示。)@ThomasBrowne很高兴听到这个消息,这无疑教会了我们如何始终对基准测试持保留态度!然而,浏览Elixir源代码发现,
min_by
在内部只使用
reduce
,因此我很困惑这比其他解决方案更快。出于好奇:在您的用例中,典型的映射和列表大小是什么?实际上,我的用例通常是相当少的列表(最多20个),但每个列表中的元素数量可能很大(在2000到3000之间)。我已经尝试了我的用例和@dogbert的地图生成器,它似乎创建了1000个小列表,在这两种情况下,你的似乎都更快。我的用例更像这样:map=Enum.into(Enum.map(1..10,fnx->{x,Enum.to_列表(1..2000+:rand.uniform(1000)))