Elixir Enum.reduce重新运行:第一次迭代后确定

Elixir Enum.reduce重新运行:第一次迭代后确定,elixir,Elixir,我在Enum.reduce中传递一个由整数组成的列表作为字符串,并对列表的每个值运行一个函数。但问题是在第二次迭代中它返回:好的,由于哪个函数给出错误,我在代码中使用多个IO.puts只是为了跟踪错误 下面是我对_decimal的函数,当我们将11作为参数传递时,它应该返回这个二进制值的十进制表示形式。但它在第二次迭代中停止,我将在下面写出输出- def to_decimal(string) do list = String.graphemes(string) len = len

我在Enum.reduce中传递一个由整数组成的列表作为字符串,并对列表的每个值运行一个函数。但问题是在第二次迭代中它返回:好的,由于哪个函数给出错误,我在代码中使用多个IO.puts只是为了跟踪错误

下面是我对_decimal的函数,当我们将11作为参数传递时,它应该返回这个二进制值的十进制表示形式。但它在第二次迭代中停止,我将在下面写出输出-

def to_decimal(string) do
    list = String.graphemes(string)
    len = length(list)
    Enum.reduce(list,0, fn x,acc ->
            temp=String.to_integer(x)
            power = round(:math.pow(2,len-1))
            IO.puts(power)
            acc = acc + temp*power
            IO.puts(acc)
            len=len-1
            IO.puts(len)
            end)
end

iex(1)> Binary.to_decimal("11")
2
2
1
2
** (ArithmeticError) bad argument in arithmetic expression: :ok + 2
    :erlang.+(:ok, 2)
    binary.exs:16: anonymous fn/3 in Binary.to_decimal/1
    (elixir) lib/enum.ex:1925: Enum."-reduce/3-lists^foldl/2-0-"/3
您应该在每次迭代中返回新的acc。当前,回调函数中最有趣的最后一个表达式是IO.puts,返回:ok。您应该在每个迭代中构建新的acc,并返回它,它将在下一个迭代中使用

请注意,在Elixir中,数据结构是不可变的。还要注意的是,您没有更改上范围中存在的len。基本上你要做的就是扔掉=len-1

如果您想了解有关变量范围的更多信息,请查看。

您应该在每次迭代中返回新的acc。当前,回调函数中最有趣的最后一个表达式是IO.puts,返回:ok。您应该在每个迭代中构建新的acc,并返回它,它将在下一个迭代中使用

请注意,在Elixir中,数据结构是不可变的。还要注意的是,您没有更改上范围中存在的len。基本上你要做的就是扔掉=len-1

如果您想了解有关变量作用域的更多信息,请查看。

使用枚举。减少使用枚举的方式并不是解决问题的最佳方法

这是因为Enum.reduce的每次迭代都应返回下一次迭代的累加器

当您将acc=acc+temp*power和len=len-1设置为acc=acc+temp*power且len=len-1时,您并没有将acc和len设置为在下一次迭代中(仅在当前迭代中)给定的值,事实上,您应该会收到关于未使用这些变量的警告

在你的块的末尾,因为你做的最后一件事是len=len-1,而且len永远都是一样的,在输入11的情况下,你传递给下一个迭代的总是1

当您添加IO.puts/1时,在最后,您将传递:ok IO.puts的返回到下一次迭代,这将产生您看到的错误

使用Enum.reduce的另一种方法是保留一个元组{len,sum},并在块的末尾始终放置len和sum的新值

因此,您可以将其更改为:

def to_decimal(string) do
  list = String.graphemes(string)
  len = length(list)

  Enum.reduce(list, {0, len}, fn x, {sum, len} ->
    temp = String.to_integer(x)
    power = :math.pow(2, len - 1) |> round()
    sum = sum + temp * power
    len = len - 1

    {sum, len}
  end)
end
这将在最后返回一个包含{n,l}的元组,其中n是十进制表示,l是最终长度。

使用Enum。减少您的操作方式并不是解决问题的最佳方法

这是因为Enum.reduce的每次迭代都应返回下一次迭代的累加器

当您将acc=acc+temp*power和len=len-1设置为acc=acc+temp*power且len=len-1时,您并没有将acc和len设置为在下一次迭代中(仅在当前迭代中)给定的值,事实上,您应该会收到关于未使用这些变量的警告

在你的块的末尾,因为你做的最后一件事是len=len-1,而且len永远都是一样的,在输入11的情况下,你传递给下一个迭代的总是1

当您添加IO.puts/1时,在最后,您将传递:ok IO.puts的返回到下一次迭代,这将产生您看到的错误

使用Enum.reduce的另一种方法是保留一个元组{len,sum},并在块的末尾始终放置len和sum的新值

因此,您可以将其更改为:

def to_decimal(string) do
  list = String.graphemes(string)
  len = length(list)

  Enum.reduce(list, {0, len}, fn x, {sum, len} ->
    temp = String.to_integer(x)
    power = :math.pow(2, len - 1) |> round()
    sum = sum + temp * power
    len = len - 1

    {sum, len}
  end)
end

这将在最后返回一个包含{n,l}的元组,其中n是十进制表示,l是最终长度。

对于实际代码,您应该使用预卷解决方案:

String.to_integer(string, 2)
也就是说,让我们把这当作一个练习来学习更多关于长生不老药的知识。Elixir的一个常见缺陷是对变量范围的误解。Elixir中的变量是不可变的,并且在作用域中总是局部的,但它们可以反弹到新的值。这使得刚开始时很难理解,因为看起来可以更改值,但实际上只是将变量名绑定到一个新值。这意味着原始范围仍将绑定到相同的值:

counter = 0
for i <- 1..10 do
  counter = counter + 1
end
IO.inspect counter # prints 0
为了解决这个问题,正如您已经正确地观察到的,我们使用这样的函数,允许我们将中间结果存储在累加器中。所以每个需要记忆的变量都必须进入累加器。然后从匿名函数返回下一次迭代的值,这允许Enum.reduce将其传递回下一次迭代

在您的例子中,这意味着您需要记住len和sum,我们可以将它们放入一个元组中,作为{sum,len}传递。r的基本结构 教育应:

result = Enum.reduce list, {0, len}, fn {sum, len} ->
  new_sum = sum + ...
  new_len = ...
  {new_sum, new_len}
end
{sum, _} = result
sum
如果你想更进一步,更好地了解所有这些部分是如何组合在一起的,我强烈建议你阅读本书的前几章。它包括一些练习来从头开始构建诸如Enum.reduce之类的实用程序,这非常有帮助

最后一句话,代码中还有一些地方需要改进。例如,它将愉快地接受无效数字,如2。作为一个灵感,以下是我将如何解决这个问题:

string
|> String.graphemes
|> Enum.reverse
|> Enum.with_index
|> Enum.reduce(0, fn
  {"0", _}, acc -> acc
  {"1", i}, acc -> acc + :math.pow(2, i)
end)
|> trunc()

我已经听到一些声音在尖叫:但是现在你在列表上重复了很多次,我会回答说,只要这不是一个瓶颈,性能优化的清晰度就没有问题。如果您真的发现需要从中挤出最后一点性能,通常最好编写一个完全不依赖于枚举模块的尾部递归函数,但我将让您自己去探索。

对于实际代码,您应该使用预编译的解决方案:

String.to_integer(string, 2)
也就是说,让我们把这当作一个练习来学习更多关于长生不老药的知识。Elixir的一个常见缺陷是对变量范围的误解。Elixir中的变量是不可变的,并且在作用域中总是局部的,但它们可以反弹到新的值。这使得刚开始时很难理解,因为看起来可以更改值,但实际上只是将变量名绑定到一个新值。这意味着原始范围仍将绑定到相同的值:

counter = 0
for i <- 1..10 do
  counter = counter + 1
end
IO.inspect counter # prints 0
为了解决这个问题,正如您已经正确地观察到的,我们使用这样的函数,允许我们将中间结果存储在累加器中。所以每个需要记忆的变量都必须进入累加器。然后从匿名函数返回下一次迭代的值,这允许Enum.reduce将其传递回下一次迭代

在您的例子中,这意味着您需要记住len和sum,我们可以将它们放入一个元组中,作为{sum,len}传递。您的减排基本结构应为:

result = Enum.reduce list, {0, len}, fn {sum, len} ->
  new_sum = sum + ...
  new_len = ...
  {new_sum, new_len}
end
{sum, _} = result
sum
如果你想更进一步,更好地了解所有这些部分是如何组合在一起的,我强烈建议你阅读本书的前几章。它包括一些练习来从头开始构建诸如Enum.reduce之类的实用程序,这非常有帮助

最后一句话,代码中还有一些地方需要改进。例如,它将愉快地接受无效数字,如2。作为一个灵感,以下是我将如何解决这个问题:

string
|> String.graphemes
|> Enum.reverse
|> Enum.with_index
|> Enum.reduce(0, fn
  {"0", _}, acc -> acc
  {"1", i}, acc -> acc + :math.pow(2, i)
end)
|> trunc()
我已经听到一些声音在尖叫:但是现在你在列表上重复了很多次,我会回答说,只要这不是一个瓶颈,性能优化的清晰度就没有问题。如果您真的发现需要从中挤出最后一点性能,通常最好编写一个完全不依赖于Enum模块的尾部递归函数,但我将把这留给您来研究