在列表推导中重新分配变量

时间:2015-09-06 11:19:24

标签: functional-programming list-comprehension elixir

我是Elixir的新手和来自OO背景的函数式编程。

我试图理解列表理解中Elixir中的变量重新分配是如何工作的。我期望函数test1()和test2()打印4,但test2()没有重新分配变量并打印1。

defmodule SampleCode do
  def test1 do
    num = 1
    num = num + 1
    num = num + 2
    IO.puts num # Prints 4
  end

  def test2 do
    num = 1
    for i <- (1..2) do
      num = num + i
    end
    IO.puts num # Prints 1
  end
end
  1. 为什么这些功能的表现不同?
  2. 这是Elixir中的变量范围内容还是函数式编程的基本原则,我不知道?

2 个答案:

答案 0 :(得分:4)

  

这是Elixir中的变量范围内容还是我缺少的函数式编程的基本原理?

实际上都是。

Elixir仅允许在同一范围和所有构造中重新绑定,${#session.['CurrentUser'].farsiFirstName} ${#session.CurrentUser.farsiFirstName} casecond除外,引入新范围。一些例子:

receive

玩意儿:

num = 1

try do
  num = 2
after
  num = 3
end

num #=> 1

现在规则例外的一些例子:

num = 1
(fn -> num = 2 end).()
num #=> 1

尽管如此,上述情况有些不鼓励,因为最好明确地返回值:

num = 1
case true do
  true -> num = 2
end
num #=> 2

num = 1
cond do
  true -> num = 2
end
num #=> 2

上面的示例明确了从case返回的值是什么。为什么那些在Elixir中得到支持,即使不推荐,也是一个长篇故事,它起源于Erlang,并且由于Elixir中的一些(极少数)命令性宏,如num = 1 case x do true -> 2 false -> num end #=> will return 1 or 2 if而继续存在。随着我们向Elixir 2.0迈进,它可能会发生变化。

执行所需内容的最佳方式是通过unless中的功能。您的特定示例可以使用Enum完成,但任何其他复杂示例都可以使用Enum.sum/1来实现(Enum中的几乎所有函数都是以reduce语言实现的,可能会在其他语言中实现)。

答案 1 :(得分:4)

要了解发生了什么,请查看elixir对您的代码所做的操作,以便在语法*不支持时语法允许重新绑定:

         BEFORE                         AFTER

defmodule SampleCode do     |  defmodule SampleCode do
  def test1 do              |    def test1 do
    num = 1                 |      num_1 = 1
    num = num + 1           |      num_2 = num_1 + 1
    num = num + 2           |      num_3 = num_2 + 2
    IO.puts num # Prints 4  |      IO.puts num_3
  end                       |    end
                            |
  def test2 do              |    def test2
    num = 1                 |      num_1 = 1
    for i <- (1..2) do      |      for i <- (1..2) do
      num = num + i         |        num_2 = num_1 + i
    end                     |      end
    IO.puts num # Prints 1  |      IO.puts num_1
  end                       |    end
end                         |  end

* Elixir的基础是Erlang-Elixir的编译产生Erlang .beam文件,由Erlang虚拟机执行。 Erlang不允许变量重新绑定,因此 Elixir在编译期间交换变量名称

如果你想要提高你的FP游戏,并在编写Elixir时给自己一个巨大的支持,我建议你强迫自己学习Erlang语法,并在一个月或几个月内严格写出你的问题的Erlang解决方案在回到Elixir之前。

如果你很好奇你的代码实际发生了什么,这里是Elixir生成的实际Erlang:

1: test1() ->
2:     num@1 = 1,
3:     num@2 = num@1 + 1,
4:     num@3 = num@2 + 2,
5:    'Elixir.IO':puts(num@3).
6: test2() ->
7:     num@1 = 1,
8:     'Elixir.Enum':reduce(#{'__struct__' => 'Elixir.Range',
9:             first => 1, last => 2},
10:          [], fun (i@1, _@1) -> num@2 = num@1 + i@1 end),
11:    'Elixir.IO':puts(num@1).

特别注意第11行 - 它引用的唯一变量是num@1,在Erlang中不能被第8-10行更改 - 无论在那里发生什么,绑定到num@1的值仍然是1,因为它是第一行绑定在第7行的。