绑定与分配

时间:2018-01-04 17:03:03

标签: ruby binding functional-programming elixir variable-assignment

我已经阅读了很多关于assignmentbinding之间差异的文章,但它还没有被点击过(特别是在命令式语言与没有语言的情况下)突变)。

我在IRC问过,有人提到这两个例子说明了区别,但后来我不得不去看,我没有看到完整的解释。

有人可以详细解释一下如何/为何如此有效,以帮助说明差异?

红宝石

x = 1; f = lambda { x }; x = 2; f.call
#=> 2

药剂

x = 1; f = fn -> x end; x = 2; f.()
#=> 1

5 个答案:

答案 0 :(得分:3)

之前我已经听过这个解释,看起来很不错:

  

您可以将绑定视为行李箱上的标签,并将其指定为   手提箱。

在其他语言中,如果您有作业,则更像是在行李箱中放置一个值。实际上,您可以更改行李箱中的值并输入不同的值。

如果您的行李箱中有值,请在 Elixir 中添加标签。您可以更改标签,但行李箱中的值仍然相同。

所以,例如:

iex(1)> x = 1
iex(2)> f = fn -> x end
iex(3)> x = 2
iex(4)> f.()
1
  1. 您的行李箱中有1,并标有x
  2. 然后你说,"在这里,功能先生,我希望你在给我打电话时告诉我这个行李箱里的东西。"
  3. 然后,您将标签从包含1的行李箱中取出,并将其放在另一个包含2的行李箱中。
  4. 然后你说"嘿,功能先生,那个行李箱里有什么东西?"
  5. 他会说" 1",因为行李箱没有改变。虽然,你已经取下它的标签并把它放在另一个手提箱上。

答案 1 :(得分:1)

Elixir vs Ruby可能不是最好的对比。在Elixir,我们可以随时重新分配"先前分配的命名变量的值。您提供的两个匿名函数示例演示了两种语言如何在其中分配局部变量的区别。在Ruby中,分配了变量(意味着内存引用),这就是为什么当我们更改它时,匿名函数返回存储在该内存引用中的当前值。在Elixir中,定义匿名函数时的变量值(而不是内存引用)被复制并存储为局部变量。

在Erlang,Elixir" parent"但是,变量作为一项规则受到限制。"一旦您声明了名为X的变量的值,就不允许为程序的其余部分更改它,并且需要将任何所需的更改存储在新的命名变量中。 (有一种方法可以在Erlang中重新分配命名变量,但它不是自定义变量。)

答案 2 :(得分:1)

理解差异的最简单方法是比较语言解释器/编译器用来生成机器/字节码的AST

让我们从ruby开始吧。 Ruby没有提供开箱即用的AST查看器,所以我将使用RubyParser gem:

> require 'ruby_parser'
> RubyParser.new.parse("x = 1; f = -> {x}; x = 2; f.()").inspect

#=> "s(:block, s(:lasgn, :x, s(:lit, 1)), 
#    s(:lasgn, :f, s(:iter, s(:call, nil, :lambda), 0, s(:lvar, :x))),
#    s(:lasgn, :x, s(:lit, 2)), s(:call, s(:lvar, :f), :call))"

我们要寻找的是第二行中的最新节点:proc 中有 x变量。换句话说,ruby期望那里的绑定变量,名为x。在评估proc时,x的值为2。因此,proc返回2

现在让我们检查一下Elixir。

iex|1 ▶ quote do
...|1 ▶   x = 1
...|1 ▶   f = fn -> x end
...|1 ▶   x = 2
...|1 ▶   f.()
...|1 ▶ end

#⇒ {:__block__, [],
# [
#   {:=, [], [{:x, [], Elixir}, 1]},
#   {:=, [], [{:f, [], Elixir}, {:fn, [], [{:->, [], [[], {:x, [], Elixir}]}]}]},
#   {:=, [], [{:x, [], Elixir}, 2]},
#   {{:., [], [{:f, [], Elixir}]}, [], []}
# ]}

第二行中的最后一个节点是我们的。它仍包含x,但在编译阶段,此x将被评估为其当前分配的值。也就是说,fn -> not_x end会导致编译错误,而在ruby中,proc体内可能存在任何内容,因为在调用时它将被评估。

换句话说,Ruby使用当前调用者的上下文来评估proc,而Elixir使用闭包。它抓住它遇到函数定义的上下文,并使用它来解析所有局部变量。

答案 3 :(得分:1)

过了一会儿,我想出了答案,这可能是“绑定”和“任务”之间差异的最佳解释;它与我在另一个答案中所写的内容没有任何共同之处,因此它作为一个单独的答案发布。

在任何函数语言中,一切都是不可变的,术语“绑定”和“赋值”之间没有任何有意义的区别。有人可能称之为;常见的模式是使用“绑定”一词,明确表示它是一个值绑定到一个变量。例如,在Erlang中,人们不能反弹变量。在Elixir中,这是可能的(为什么,为了上帝的缘故,José,为什么?)

在Elixir中考虑以下示例:

iex> x = 1
iex> 1 = x

以上是完全有效的Elixir代码。很明显,人们无法为 one 分配任何东西。它既不是任务也不是约束力。它是匹配。这就是在Elixir(和Erlang)中处理=的方式:a = b如果将绑定到不同的值,则会失败;如果它们匹配则返回RHO;如果LHO尚未绑定,它会将LHO绑定到RHO。

在Ruby中它有所不同。 在分配(复制内容)和绑定(制作引用)之间存在显着差异。

答案 4 :(得分:0)

绑定指的是基于表达式的语言中使用的特定概念,如果您习惯使用基于语句的语言,这些概念可能看起来很陌生。我将使用ML风格的示例来演示:

let x = 3 in
   let y = 5 in
       x + y

val it : int = 8

此处使用的let... in语法表明绑定let x = 3仅限于in之后的表达式。同样,绑定let y = 5仅限于表达式x + y,这样,如果我们考虑另一个示例:

let x = 3 in
   let f () =
       x + 5
   let x = 4 in
       f()

val it : int = 8

结果仍是8,即使我们在let x = 4的调用之上有绑定f()。这是因为f本身绑定在绑定let x = 3的范围内。

基于语句的语言中的赋值是不同的,因为分配的变量不是作用于特定表达式的范围,它们实际上是全局的'对于他们所处的任何代码块,所以重新分配变量的值会改变使用相同变量的评估结果。