【问题标题】:Elixir lists with tuples modifications带有元组修改的 Elixir 列表
【发布时间】:2020-05-16 01:28:08
【问题描述】:

我正在尝试创建以下列表:

["extract-audio", "audio-format", "mp3"]

基于:

options = ["extract-audio", "audio-format": "mp3"]

我的代码从提供的options 移动到首选列表:

arguments = Enum.map(options, fn(option) ->
      parametrize(option)
    end) |> List.flatten

def parametrize(arg) when is_tuple(arg), do: Tuple.to_list(arg)
def parametrize(arg) when is_binary(arg), do: arg

返回的列表几乎是正确的:

["extract-audio", :"audio-format", "mp3"]

但是,我不太明白audio-format前面的分号在做什么。创建某种正则表达式来代替它感觉有点不对劲。有没有比我目前的实现更好的解决方案?我也考虑过地图,但在某些情况下,我只想要一个参数而不是键值。

【问题讨论】:

  • :"audio-format" 看起来像一个原子
  • @JohnColeman 是的,我们可以使用 `Atom.to_String(:"audio-format") 轻松转换它。但我真的不想遍历列表中的所有项目以查看是否需要转换它。
  • 使用模式匹配而不是is_tuple?

标签: elixir


【解决方案1】:

您不需要任何帮助程序,只需使用带有多个子句的 reducer。下面的代码需要遍历列表,不需要展平。

options = ["extract-audio", "audio-format": "mp3"]

options
|> Enum.reduce([], fn            
  {k, v}, acc -> [v, k | acc]
  e, acc -> [e | acc]                   
end)
|> Enum.reverse()

#⇒ ["extract-audio", :"audio-format", "mp3"]

您可能会注意到,:"audio-format" 是一个原子。那是因为 Elixir 做了一些神奇的构建 Keyword 而不是 "foo": "bar

["a-b": 42] == [{:"a-b", 42}]
#⇒ true

【讨论】:

  • 不错。似乎他们可能更喜欢{k, v}, acc -> [v, Atom.to_String(k) | acc]
  • @JohnColeman 可能是,但它会损坏原始值,所以我暂时保留它。我放弃了一个澄清句。
  • @KevinEtore 反转列表是 O(n),但通过小连接构建列表是 O(n^2)。对于大型列表,差异是显而易见的。
  • @JohnColeman 这并不完全正确。 [v, k] ++ acc 而不是 [v, k | acc] 不会导致 O(n²)erlang.org/doc/efficiency_guide/…
  • @AlekseiMatiushkin 好点,尽管这种方法仍然需要reverse。感谢您的链接。我刚从 Elixir 开始,但有 SML 的背景,尽管语法和类型规则不同,但它与基本的函数式编程有相似的方法。在 SML 中很常见的做法是通过将项目放在列表的前面然后将其反转来构建列表。
【解决方案2】:

我不确定这是否有效,但这只是有效

替换

def parametrize(arg) when is_tuple(arg), do: Tuple.to_list(arg)

def parametrize(arg) when is_tuple(arg) do
  for el <- Tuple.to_list(arg) do
     if is_atom(el), do: Atom.to_string(el), else: el
  end
end

【讨论】:

  • if in elixir 是一种代码味道,特别是当它绝对是多余的时候。
【解决方案3】:

有多种实现方式

1.递归

def parameterize(list, acc \\ [])
def parameterize([{k, v} | tail], acc) do
  parameterize(tail, [v, k | acc])
end

def parameterize([element | tail], acc) do
  parameterize(tail, [element | acc])
end

def parameterize([], acc), do: acc |> List.flatten |> Enum.reverse()

2。使用上面 Aleksei Matiushkin 回答的 Enum.reduce

options = ["extract-audio", "audio-format": "mp3"]

options
|> Enum.reduce([], fn            
  {k, v}, acc -> [v, k | acc]
  e, acc -> [e | acc]                   
end)
|> Enum.reverse()

#⇒ ["extract-audio", :"audio-format", "mp3"]

3.使用 Enum.flat_map

Enum.flat_map(options, fn
  value when is_tuple(value) -> Tuple.to_list(value)
  any -> [any]
end)

根据舒适度可以使用这三个中的任何一个:D

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-04-26
    • 2019-10-29
    • 1970-01-01
    • 2022-11-21
    • 2015-05-11
    • 2010-11-17
    • 1970-01-01
    相关资源
    最近更新 更多