当我创建一个数组A并将其分配给B
时A = [1:10]
B = A
我可以修改A,更改反映在B
中A[1] = 42
# B[1] is now 42
但是,如果我使用标量变量,那么变化不会传播:
a = 1
b = a
a = 2
# b remains being 1
我甚至可以将事物混合起来并将矢量转换为标量,并且变化不会传播:
A = [1:10]
B = A
A = 0
# B remains being 1,2,...,10
=
运营商到底做了什么?当我想复制变量并修改旧变量以保留新变量的完整性时,我应该何时使用b = copy(a)
而不是b=a
?
答案 0 :(得分:28)
混淆源于此: 赋值 和变异不是一回事。
分配。分配看起来像x = ...
- =
左边的是标识符,即变量名称。对象变量x
引用的赋值更改(这称为变量绑定)。它根本不会改变任何对象。
突变。在Julia中有两种典型的变异方式:
x.f = ...
- =
左边的内容是字段访问表达式;
x[i] = ...
- =
左边的内容是索引表达式。目前,字段突变是基本的 - 语法只能 意味着您通过更改其字段来改变结构。这可能会改变。数组变异语法不是基础 - x[i] = y
表示setindex!(x, y, i)
,您可以向setindex添加方法!或在本地更改哪个泛型函数setindex!
。实际的数组赋值是一个内置函数 - 一个用C实现的函数(我们知道如何生成相应的LLVM代码)。
Mutation改变对象的值;它不会改变任何变量绑定。执行上述任一操作后,变量x
仍然引用之前执行的相同对象;但是,该对象可能具有不同的内容。特别是,如果该对象可以从其他一些范围访问 - 比如调用一个进行变异的函数 - 则更改的值将在那里可见。但是没有绑定发生变化 - 所有范围内的所有绑定仍然引用相同的对象。
你会注意到,在这个解释中,我从未谈过可变性或不变性。这是因为它与任何这一点无关 - 可变和不可变对象在赋值,参数传递等方面具有完全相同的语义。唯一的区别是,如果你尝试{{1}当x是不可变的时,你会得到一个错误。
答案 1 :(得分:-4)
此行为与Java类似。 A和B是可以容纳"普通"数据类型,例如整数,浮点数等,或者是指向更复杂数据结构的引用(也称为指针)。与Java相比,Julia将许多非抽象类型处理为" plain"数据
您可以使用isbits(A)
测试变量A是否包含位值,或者包含对另一个数据对象的引用。在第一种情况下,B=A
会将A
中的每一位复制到B
的新内存分配,否则,只会复制对该对象的引用。
还可以使用pointer_from_objref(A)
。