在 Julia 的函数中重新分配字典值

Posted

技术标签:

【中文标题】在 Julia 的函数中重新分配字典值【英文标题】:Reassignment of Dictionary Values inside a Function in Julia 【发布时间】:2020-09-04 11:33:57 【问题描述】:

Julia 新手,因此可能是一个基本问题。

x = 1
function someFn()
   print(x)
   x = 3 
end

这会在 print(x) 处引发错误,因为在函数内部看不到全局 x inside。这是有道理的。

x = [1,2]
function someFn()
   print(x)
   x[1] = 4
end
print(x)

这里 print(x) 可以看到全局数组 x 和 x[1]=4 全局更改全局 x 的值。当我将 x 设为字典时,会观察到类似的行为。

三个问题

    变量/数组的这种行为及其在函数内的作用域是否与 Julia 的工作方式一致? 当 x 是一个数组时,即使没有将引用作为输入传递给函数,它在函数内部也是可见的。这是正确的吗? 此外,更改数组中条目的值会全局反映。是不是因为 Julia 在任何地方都将数组 x 视为引用?

【问题讨论】:

是的,是的,而且差不多。请注意,用于数组的全局名称和用于函数的全局名称(如print)之间没有区别。 docs.julialang.org/en/v1/manual/variables-and-scoping/… 感谢马特的评论。突出显示的引用不会捕获变量和其他数据类型(如数组/字典/函数)之间的差异(正如您所提到的)。出于好奇,在 Julia 文档中是否有参考可以捕捉到我的问题的本质?我曾尝试寻找但未能找到一个。感谢您的帮助。 【参考方案1】:

让我评论一下我对这个问题的理解。

首先,变量名指向值。在变量名和值之间进行绑定的最基本方法是赋值形式:

variable_name = value

请注意,在= 的左侧,唯一存在的就是变量名,这一点至关重要。

(旁注:除了=之外,还有其他方法可以进行绑定,例如+=等或函数定义;但让我们在这里关注问题的核心)

现在,如果您看到=,这并不意味着它是一个创建新绑定的赋值。特别是:

variable_name[index] = value

不是赋值操作(创建新绑定)而是setindex! 操作。写variable_name[index] = value和写setindex!(variable_name, value, index是一样的。

(旁注:这是真的,除了一些特殊情况,例如处理beginend,由@view 宏处理等,但这在讨论中并不重要)

现在根据这些 cmets 直接回答您的问题:

变量/数组的这种行为及其在函数内的作用域是否与 Julia 的工作方式一致?

是的。但请注意,您会收到以下错误:

x = 1
function someFn()
   print(x)
   x = 3 
end

因为 Julia 知道 x = 3 存在于函数体中,这使得 Julia 甚至在绑定到值之前就知道 x 是一个局部变量(变量是局部的或全局的)。

这是一个更极端的例子:

julia> x = 10
10

julia> function f()
       if true
           println(x)
       else
           x = 5
       end
       end
f (generic function with 1 method)

julia> f()
ERROR: UndefVarError: x not defined

虽然我们知道else 之后的分支永远不会执行,但它仍然使变量x 成为本地变量。

当 x 是一个数组时,即使没有将引用作为输入传递给函数,它在函数内部也是可见的。这是正确的吗?

这是可见的,因为x[1] = 4 不会创建名为x 的局部变量,而只是对setindex! 函数的调用。所以本地没有定义x变量,所以使用了全局变量x

此外,更改数组中条目的值会全局反映。是不是因为 Julia 在任何地方都将数组 x 视为引用?

这里一定要记住x 不是一个数组。它是一个绑定到数组的变量。然后,再次,x[1] = 4 与您编写 setindex!(x, 4, 1) 相同,因此您只需在变量 x 绑定到的值(本例中为数组)上调用函数。这是可行的,因为 Julia 中的作用域规则表明,如果作用域中没有给定名称的局部变量,则 Julia 会在全局作用域中搜索该名称。

最后让我评论一下,不鼓励在 Julia 中使用全局变量,因为这很慢。因此,尽管本次讨论与了解 Julia 的工作方式高度相关,但实际上您几乎不需要知道这一点(您将在 99.99% 的时间使用局部变量,如果您使用全局变量,则最好使用它们const)。

【讨论】:

感谢您的详细回答,澄清了很多。我想进一步澄清(如果可能的话)你的最终评论。我知道不鼓励使用 global。现在让我谈谈我正在尝试构建的内容:我正在构建一个包含多个子单元的系统,这些子单元相互通信并相互报告它们的状态。我已将这些子单元的状态打包在单独的字典中。为了绕过全局,您是否建议我将这些子单元的状态作为输入发送给我调用的许多函数? 是的 - 要么将状态作为输入传递,要么将这些 dicts const。我个人会选择传递变量 - 因为它通常更清楚发生了什么(在函数内部你有 100% 清楚它的工作原理 - 只有它的参数),但使用 const 也是一种有效的方法,有时是使用(通常表示模块的一些全局状态)。

以上是关于在 Julia 的函数中重新分配字典值的主要内容,如果未能解决你的问题,请参考以下文章

将函数重新分配到一个值

js从一个函数重新分配一个值给多个已经声明的变量[重复]

编辑后如何在活动的 Julia 会话中重新加载模块?

摆脱 Julia 的“警告:为未更改的字符串重新定义常量”?

指定 Julia 函数只能采用其内容为特定类型的字典/数组的正确方法是啥?

如何在函数中重新分配结构数组