如何获得列表的排列?

时间:2015-11-17 12:00:59

标签: elixir

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

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

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

5 个答案:

答案 0 :(得分:9)

像这样:

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

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

答案 1 :(得分:5)

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

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

答案 2 :(得分:0)

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

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

答案 3 :(得分:0)

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

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"]]]
#  ]

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

答案 4 :(得分:0)

我(重新)写了这篇文章,以更好地理解上面的答案:

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```