自定义展平实施中的空头

时间:2019-04-03 12:07:56

标签: elixir

因此,基本上我尝试使用一些嵌套的递归自己实现flatten函数。问题是我遇到了无限循环,当检查实际的磁头时它是空的。我刚开始学长生不老药,所以请不要对我太苛刻。

defmodule Test do
  def custom_flatten list do
    custom_flatten list, []
  end
  def custom_flatten [h|t], accumulator do
    custom_flatten [t], [custom_flatten(h) | accumulator]
  end
  def custom_flatten [], accumulator do
    IO.puts "Called empty list match #{accumulator}"
    Enum.reverse accumulator
  end

  def custom_flatten h, accumulator do
    [h|accumulator]
  end
end

2 个答案:

答案 0 :(得分:0)

此行会导致您出现问题:

    custom_flatten [t], [custom_flatten(h) | accumulator]

如果函数以[h | t] = [1, 2, 3]开头,则t将为[2, 3],并将[[2, 3]]传递给下一个函数。该函数将显示[h | t] = [[2, 3]],因此h将是[2, 3],而t将是[],这会将[[]]传递给下一个函数。然后,您的函数将陷入重复[h | t] = [[]]的困境,其中h[],而t[]

因此,实际上唯一的问题是您不想包装t。用类似这样的方法替换上面的行应该可以解决无限递归问题。

    custom_flatten t, [custom_flatten(h) | accumulator]

答案 1 :(得分:0)

您似乎已经在这样做了,但是一个好的策略是查看官方实现的工作。 Elixir的实现仅calls throughErlang implementation。这是用Elixir重写的Erlang实现:

def flatten([], acc), do: acc

def flatten([h | t], acc) when is_list(h) do
  flatten(h, flatten(t, acc))
end

def flatten([h | t], acc) do
  [h | flatten(t, acc)]
end

似乎您的实现存在一些问题。

  1. 终止条件是第一个参数为空列表时。但是正如布雷特(Brett)所述,由于递归调用通过[t](这是一个包含尾部列表的单元素列表),所以终止条件永远不会发生。
  2. 您需要使用guards来检测下一个元素(h)是否为列表,以决定是否需要另一层递归,或者是否可以直接使用该元素。
  3. 您的自定义行为似乎在建立反向列表。在这种情况下,您需要使用串联(++)运算符,因为在反向构建时,我们必须动态地将子列表展平,然后将它们连接到累加器(在非反向的Erlang中)实施中,尾部总是先被拉平,因此在这种情况下没有必要)。我们还需要确保只反转最终结果,否则内部列表在递归调用期间也会被反转:
def flatten(list) do
  list
  |> flatten([])
  |> Enum.reverse()
end

def flatten([], acc), do: acc

def flatten([h | t], acc) when is_list(h) do
  flatten(t, flatten(h, []) ++ acc)
end

def flatten([h | t], acc) do
  flatten(t, [h | acc])
end