Function 朱莉娅·斯考平:为什么这个函数要修改一个全局变量?

Function 朱莉娅·斯考平:为什么这个函数要修改一个全局变量?,function,arguments,julia,scoping,Function,Arguments,Julia,Scoping,对于朱莉娅来说,我是一个相对较新的人,到目前为止,我是它的粉丝。但是经过多年的R编程,一些范围规则让我感到困惑 让我们来看看这个函数。这完全符合我的预期 function foo1(x) y = x t = 1 while t < 1000 t += 1 y += 1 end return 42 end var = 0; foo1(var) # 42 var # 0 函数foo1(x) y=x t=1 而t

对于朱莉娅来说,我是一个相对较新的人,到目前为止,我是它的粉丝。但是经过多年的R编程,一些范围规则让我感到困惑

让我们来看看这个函数。这完全符合我的预期

function foo1(x)
    y = x
    t = 1
    while t < 1000
      t += 1
      y += 1
    end
    return 42
end

var = 0;
foo1(var)
# 42
var
# 0
函数foo1(x)
y=x
t=1
而t<1000
t+=1
y+=1
结束
返回42
结束
var=0;
foo1(变量)
# 42
变量
# 0
但在数组上执行类似操作时,它充当一个变异函数(在全局范围内修改它的参数!)

函数foo2(x)
y=x
t=1
而t<1000
t+=1
y[1]+=1
结束
返回42
结束
var=零(1);
foo2(变量)
# 42
变量
# 999.0

我意识到我可以通过将第一行改为
y=copy(x)
,来解决这个问题,但是这种(危险的?)行为的原因是什么呢?

我会写一个答案,但我认为约翰·迈尔斯·怀特已经比我做得更好了,所以我将链接到他的博客:


简而言之,
x=1
x[1]=1
是非常不同的操作。第一个是赋值,即更改变量
x
的绑定,而第二个是调用
setindex函数,对于数组,该函数将分配给数组中的一个位置。赋值只更改引用哪些对象的变量,从不修改任何对象。突变只会修改对象,而不会更改哪些变量引用哪些对象。这个答案有一点更详细的区别:。

简而言之,
x=1
x[1]=1
是非常不同的操作。第一个是赋值(更改
x
的绑定),而第二个是
setproperty的语法糖函数。谢谢你的指针,确实很有启发性。我对帖子的语气感到惊讶,这似乎暗示修改函数的参数是可取的。我想我需要忘记Julia中的函数编程(“无副作用”),接受效率。惯例是,如果函数改变其参数,函数名应以
结尾
以警告用户该错误。函数通常有变异和纯功能变体,以便在必要时提高效率,但默认情况下提供更安全、不变异的行为。太好了,感谢您的澄清。在Julia中编写安全、功能纯函数的正确方法是首先
copy()
函数体中可能修改的参数吗?如果您有一个最容易表达和/或执行起来最有效的算法,那么就这样做!将其定义为
函数算法!(x) 。。。结束
。然后,您可以定义一个辅助
算法(x)=算法!(copy(x))
如果这有意义的话。这种行为与变量范围无关,它是关于突变和赋值不一样的事实:
function foo2(x)
    y = x
    t = 1    
    while t < 1000
      t += 1
      y[1] += 1
    end
    return 42
end

var = zeros(1);
foo2(var)
# 42
var
# 999.0