【问题标题】:How can I get permutations of a list?如何获得列表的排列?
【发布时间】:2016-02-18 19:00:27
【问题描述】:

如何在 Elixir 中获取列表的排列?

例如,对于["a", "b", "c"],我希望:

# [["a", "b", "c"], ["a", "c", "b"], 
# ["b", "a", "c"], ["b", "c", "a"],
# ["c", "a", "b"], ["c", "b", "a"]]

【问题讨论】:

标签: elixir


【解决方案1】:

像这样:

defmodule Permutations do
  def of([]) do
    [[]]
  end

  def of(list) do
    for h <- list, t <- of(list -- [h]), do: [h | t]
  end
end

【讨论】:

  • @OnorioCatenacci 这不是一个非常有用的评论。 :D 我还不是 Elixir 专家。如果您有时间发布一个更惯用的解决方案,我会很高兴看到 - 大多数情况下我把它放在这里是因为我认为这将是一个常见问题,但我没有找到关于 SO 的答案。下次我搜索时,我会的。我认为它是其他人也可以使用的“自我注释”。
  • 该评论并非真正针对您。它更多地针对可能看到您的代码并且不知道更好的其他人,认为这是编写 Elixir 代码的正确方法。
  • 那么@OnorioCatenacci 为什么它不是惯用的?我也是 Elixir 的新手,希望能解释一下这与“正确方法”之间的区别。谢谢。
  • 我没有时间提出更惯用的版本,所以这是我的问题。 1.) def ([]) 。 . .会更习惯地写成def of([]), do: [[]] 和 2.) 列表的尾部通常会被模式匹配,而不是通过 list -- [h] 完成您可能还会在参数中将列表分解为头部/尾部,如下所示:def of([h|t] = list) do
  • 我认为这里的问题是将 h/t 视为参数列表的头/尾,而它们是 for 理解中的临时变量。 rosetta stone implementation(基于 erlang 的)使用 x,y 而不是 h/t 并且可能更清晰。 PS:这里链接rosetta而不是块代码
【解决方案2】:

有一种稍微不同的方法,它还支持为结果列表指定所需的长度:

defmodule Permutations do
  def shuffle(list), do: shuffle(list, length(list))

  def shuffle([], _), do: [[]]
  def shuffle(_,  0), do: [[]]
  def shuffle(list, i) do
    for x <- list, y <- shuffle(list, i-1), do: [x|y]
  end
end

跑步:

iex(24)> Permutations.shuffle ["a", "b", "c"]
[["a", "a", "a"], ["a", "a", "b"], ["a", "a", "c"], ["a", "b", "a"],
 ["a", "b", "b"], ["a", "b", "c"], ["a", "c", "a"], ["a", "c", "b"],
 ["a", "c", "c"], ["b", "a", "a"], ["b", "a", "b"], ["b", "a", "c"],
 ["b", "b", "a"], ["b", "b", "b"], ["b", "b", "c"], ["b", "c", "a"],
 ["b", "c", "b"], ["b", "c", "c"], ["c", "a", "a"], ["c", "a", "b"],
 ["c", "a", "c"], ["c", "b", "a"], ["c", "b", "b"], ["c", "b", "c"],
 ["c", "c", "a"], ["c", "c", "b"], ["c", "c", "c"]]

iex(25)> Permutations.shuffle ["a", "b", "c"], 2
[["a", "a"], ["a", "b"], ["a", "c"], ["b", "a"], ["b", "b"], ["b", "c"],
 ["c", "a"], ["c", "b"], ["c", "c"]]

Source

【讨论】:

  • 我不确定这是不是想要的输出,有重复
【解决方案3】:

这是一个没有理解的版本:

defmodule Permute do
  def permute(_chars, building, 0) do
    [building]
  end

  def permute(chars, building, dec) do
    Stream.map(chars, fn char -> building ++ [char] end)
    |> Enum.flat_map(fn building -> permute(chars, building, dec - 1) end)
  end
end

有一个帮助函数来允许输入也是一个字符串很有用:

def permute(str) do
  permute(String.split(str, "", trim: true), [], String.length(str))
end

【讨论】:

    【解决方案4】:

    为了便于发现,我将这段代码放在这里。

    defmodule P do
      defmacro permutations(l, n) do
        clause =
          fn i -> {:<-, [], [{:"i#{i}", [], Elixir}, l]} end
        return =
          Enum.map(1..n, fn i -> {:"i#{i}", [], Elixir} end)
        Enum.reduce(1..n, return, fn i, acc ->
          {:for, [], [clause.(i), [do: acc]]}
        end)
      end
    end
    
    defmodule T do
      require P
    
      def permute3(list), do: P.permutations(list, 3)
    end
    

    它使用纯 AST 来返回嵌套列表推导。

    T.permute3 ~w|a b|  
    #⇒ [
    #    [[["a", "a", "a"], ["b", "a", "a"]],
    #     [["a", "b", "a"], ["b", "b", "a"]]],
    #    [[["a", "a", "b"], ["b", "a", "b"]],
    #     [["a", "b", "b"], ["b", "b", "b"]]]
    #  ]
    

    由于早期的Range 扩展,需要更多的努力才能将n 作为参数传递,但它仍然可行。

    【讨论】:

      【解决方案5】:

      我(重新)写这个是为了更好地理解上面的答案:

      def permutations(list) do
          if list == [] do
            [[]]
          else
            # recursively call itself on every element picked on the list and the remaining ones
            for h <- list, t <- permutations(list -- [h]) do
              [h | t]
            end
          end
        end
      

      它通过递归工作,如果列表为空,则返回一个仅包含空列表的列表(空列表的唯一可能排列)。

      如果列表不为空,则遍历其中的每个元素h,并使用剩余的元素调用自身。然后,为每个结果构建连接列表。

      让我有点困惑的是 Elixir 评估具有多个范围的 for 的方式,我在 official documentation 中找不到它,但它似乎评估了第一个值,结果可以用于第二。例如这是有效的:

      for a <-1..3, b <- 1..a*2 do
        "#{a} - #{b}"
      end```
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-04-09
        • 2021-12-23
        • 2021-07-26
        • 1970-01-01
        • 1970-01-01
        • 2022-11-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多