如何在长生不老药的循环中增加一个值

How to increment a value in a loop in elixir

我正在尝试计算从 range1 到 range4 的每个范围内的数字出现了多少次。范围 1 为 1-10,范围 2 为 11-20,范围 3 为 21-30,范围 4 为 31-40。我下面的是:

range1=0
range2=0
range3=0
range4=0
arr=[1,11,2,22,33,23]
Enum.each arr, fn(x) ->
  if x >=1 and x<=10 do
    range1=range1+1
  end
  if x>=11 and x<=20 do
    range2=range2+1
  end
  if x>=21 and x<=30 do
    range3=range3+1
  end
  if x>=31 and x<=40 do
    range4=range4+1
  end
end 

IO.puts range1
IO.puts range2
IO.puts range3
IO.puts range4

但是当我 运行 它时,它会说它们都是 0。为什么要这样做?

是一门不可变的语言。一个变量的值不能改变。曾经。

可能 重新绑定 一个变量,但它永远不会泄漏到外部范围,所以你所做的基本上是在内部范围内立即绑定 rangeN丢弃它。来自外部作用域的变量(巧合地具有相同的名称)永远不会改变。

我们在 中也没有 loops。我们使用 map/reduce 代替。

也就是说,你需要的是Enum.reduce/3

Enum.reduce(
    [1,11,2,22,33,23], # input
    {0, 0, 0, 0},      # initial accumulator
    fn x, {acc1, acc2, acc3, acc4} ->
  cond do
    x>=1 and x<=10 -> {acc1 + 1, acc2, acc3, acc4}
    x>=11 and x<=20 -> {acc1, acc2 + 1, acc3, acc4}
    x>=21 and x<=30 -> {acc1, acc2, acc3 + 1, acc4}
    x>=31 and x<=40 -> {acc1, acc2, acc3, acc4 + 1}
  end
end)
#⇒ {2, 1, 2, 1}

函数总是returns最后一个表达式的结果,因此我们在这里使用cond/1。但惯用的方法是让累积函数的多个子句带有守卫。

Enum.reduce [1,11,2,22,33,23], {0, 0, 0, 0}, fn 
  x, {acc1, acc2, acc3, acc4} when x>=1 and x<=10 ->
    {acc1 + 1, acc2, acc3, acc4}
  x, {acc1, acc2, acc3, acc4} when x>=11 and x<=20 ->
    {acc1, acc2 + 1, acc3, acc4}
  x, {acc1, acc2, acc3, acc4} when x>=21 and x<=30 ->
    {acc1, acc2, acc3 + 1, acc4}
  x, {acc1, acc2, acc3, acc4} when x>=31 and x<=40 ->
    {acc1, acc2, acc3, acc4 + 1}
end 

要获得值,可以模式匹配结果或使用 Kernel.elem/2

{less_that_10, _, _, _} =
  Enum.reduce(...)
less_than_10
#⇒ 2

input
|> Enum.reduce(...)
|> elem(0)
#⇒ 2

when I run it, it'll say that they're all 0. Why is it doing that?

Elixir 中的变量绑定到当前范围。如果您绑定到函数内部的变量(例如您在代码中传递给 Enum.each/2 的匿名函数),则该变量仅在该函数内部可用。尽管您在匿名函数中分配给 range1,但它与您在文件顶部定义的 range1 完全不同,它始终是 0.

How to increment a value in a loop in elixir

这是错误的思考方式。您需要考虑的是处理列表。对于你的情况,我认为结合 Enum.frequencies_by/2 with guards and ranges 是一个很好的解决方案:

Enum.frequencies_by([1, 11, 2, 22, 33, 23], fn
  i when i in 1..10 -> :range1
  i when i in 11..20 -> :range2
  i when i in 21..30 -> :range3
  i when i in 31..40 -> :range4
end)

这会产生:

%{range1: 2, range2: 1, range3: 2, range4: 1}

如果我可以假设你的范围是固定的,并且你的输入大于零,我提出这个解决方案:

[range1,range2,range3,range4] =
   for elem <- arr, reduce: [0,0,0,0] do
     answer -> 
       update_in(answer, [Access.at!(div((elem - 1), 10))], &(&1 + 1))
   end

Access.at!() 会方便地为输出数组范围之外的索引抛出异常。换句话说,输入 45 将生成索引 4,这超出了 [0,0,0,0]

的索引范围