在表达式内部通过引用传递表达式
Passing expressions by reference inside expressions
我对 Julia 还很陌生,如果对我的语言有任何误解,我深表歉意。我最近主要使用 Python,并大量使用 SymPy 及其代码生成功能,而具有元编程功能的 Julia 似乎专为以我喜欢的风格编写代码而构建。
特别是,我想在 Julia 中从一组较小的构建块中构建块矩阵,这些构建块之间有一些不同的操作。出于调试目的,并且因为在其他计算中使用了各种中间矩阵,我想将它们保留为包含变量的表达式,这样我就可以快速循环并测试不同的不同输入,而无需将所有内容都包装在一个函数中。
现在,对于一个最小的案例研究,假设我有两个表达式 mat1 = :a
和 mat2 = :b
,我想将它们组合起来形成一个新的第三个表达式:
mat3 = :($mat1 + $mat2)
上面的方法工作正常直到我修改了mat1
和mat2
,在这种情况下我必须重新评估mat3
为了反映这个更新。我认为,这是由于 $mat1 + $mat2
没有通过引用传递 mat1
和 mat2
,而是在评估该行时插入内部表达式引起的。我想要实现的行为是 mat1
和 mat2
在我调用 eval(mat3)
之前不会插入,最好使用最少的样板。
是否可以通过方便的语法实现这一点?
mat3
将反映 mat1
和 mat2
的 突变 ,但不会反映 重新绑定 mat1
和 mat2
。了解突变和重新绑定之间的区别很重要。
突变
修改对象的数据时发生变异。请注意,这不会影响任何 names,只会影响 objects。这可以通过多种方式体现,包括函数 push!
和具有复杂左侧的赋值语法,例如 A[1] = 5
.
例如以下都是突变的例子:
A = [1, 2, 3]
A[1] = 4
姓名A不变; A 仍然指向同一个对象。 A
代表的对象被修改了
A = :(f(x))
A.args[1] = :g
姓名A不变; A 仍然指向同一个对象。 A
代表的对象被修改了
mat1 = :(f(x))
mat2 = :(f(y))
mat3 = :($mat1 + $mat2)
mat1.args[1] = :g
名称mat1
不变;它仍然指向同一个对象。该对象已修改。 mat3
也引用了同一个对象,因为它已经被修改,所以它会反映出变化。确实,现在 mat3
包含 :(g(x) + f(y))
.
重新绑定
(也称为赋值)
重新绑定发生在没有对象数据被修改但名称的目标更改为不同对象的目标时。这由一个简单的 =
赋值表示,左侧是反弹的东西。
x = 2
x = 3
这里 x
正在从对象 2
反弹到对象 3
。我们没有 更改 对象 2
。事实上,因为 2
是一个 immutable 对象,所以不允许改变对象 2
。相反,x
的可观察值发生变化的原因是因为它现在引用了一个不同的对象:3
.
A = [1, 2, 3]
A = [4, 2, 3]
这里我们再一次没有变异向量A
;我们正在创建一个新向量,现在 A
引用这个新向量。区分突变和重新结合很重要。再一次,突变作用于 objects,重新绑定作用于 names.
mat1 = :x
mat2 = :y
mat3 = :($mat1 + $mat2)
mat1 = :z
注意这里简单赋值不会改变 mat1
引用的对象:x
;它只是将 mat1
重新绑定到不同的对象 :z
。这意味着包含对象 :x
的 mat3
不会受到影响。
请注意 Symbol
是 不可变的 类型,因此您无法更改它。因此不可能按照你的建议去做。
完成您提议的更好方法是使用函数而不是单个表达式。一个函数可以被多次调用,产生不同的对象。
mat1 = :x
mat2 = :y
mat3() = :($mat1 + $mat2) # function definition
mat3() # :(x + y)
mat1 = :z
mat3() # :(z + y)
我对 Julia 还很陌生,如果对我的语言有任何误解,我深表歉意。我最近主要使用 Python,并大量使用 SymPy 及其代码生成功能,而具有元编程功能的 Julia 似乎专为以我喜欢的风格编写代码而构建。
特别是,我想在 Julia 中从一组较小的构建块中构建块矩阵,这些构建块之间有一些不同的操作。出于调试目的,并且因为在其他计算中使用了各种中间矩阵,我想将它们保留为包含变量的表达式,这样我就可以快速循环并测试不同的不同输入,而无需将所有内容都包装在一个函数中。
现在,对于一个最小的案例研究,假设我有两个表达式 mat1 = :a
和 mat2 = :b
,我想将它们组合起来形成一个新的第三个表达式:
mat3 = :($mat1 + $mat2)
上面的方法工作正常直到我修改了mat1
和mat2
,在这种情况下我必须重新评估mat3
为了反映这个更新。我认为,这是由于 $mat1 + $mat2
没有通过引用传递 mat1
和 mat2
,而是在评估该行时插入内部表达式引起的。我想要实现的行为是 mat1
和 mat2
在我调用 eval(mat3)
之前不会插入,最好使用最少的样板。
是否可以通过方便的语法实现这一点?
mat3
将反映 mat1
和 mat2
的 突变 ,但不会反映 重新绑定 mat1
和 mat2
。了解突变和重新绑定之间的区别很重要。
突变
修改对象的数据时发生变异。请注意,这不会影响任何 names,只会影响 objects。这可以通过多种方式体现,包括函数 push!
和具有复杂左侧的赋值语法,例如 A[1] = 5
.
例如以下都是突变的例子:
A = [1, 2, 3]
A[1] = 4
姓名A不变; A 仍然指向同一个对象。 A
代表的对象被修改了
A = :(f(x))
A.args[1] = :g
姓名A不变; A 仍然指向同一个对象。 A
代表的对象被修改了
mat1 = :(f(x))
mat2 = :(f(y))
mat3 = :($mat1 + $mat2)
mat1.args[1] = :g
名称mat1
不变;它仍然指向同一个对象。该对象已修改。 mat3
也引用了同一个对象,因为它已经被修改,所以它会反映出变化。确实,现在 mat3
包含 :(g(x) + f(y))
.
重新绑定
(也称为赋值)
重新绑定发生在没有对象数据被修改但名称的目标更改为不同对象的目标时。这由一个简单的 =
赋值表示,左侧是反弹的东西。
x = 2
x = 3
这里 x
正在从对象 2
反弹到对象 3
。我们没有 更改 对象 2
。事实上,因为 2
是一个 immutable 对象,所以不允许改变对象 2
。相反,x
的可观察值发生变化的原因是因为它现在引用了一个不同的对象:3
.
A = [1, 2, 3]
A = [4, 2, 3]
这里我们再一次没有变异向量A
;我们正在创建一个新向量,现在 A
引用这个新向量。区分突变和重新结合很重要。再一次,突变作用于 objects,重新绑定作用于 names.
mat1 = :x
mat2 = :y
mat3 = :($mat1 + $mat2)
mat1 = :z
注意这里简单赋值不会改变 mat1
引用的对象:x
;它只是将 mat1
重新绑定到不同的对象 :z
。这意味着包含对象 :x
的 mat3
不会受到影响。
请注意 Symbol
是 不可变的 类型,因此您无法更改它。因此不可能按照你的建议去做。
完成您提议的更好方法是使用函数而不是单个表达式。一个函数可以被多次调用,产生不同的对象。
mat1 = :x
mat2 = :y
mat3() = :($mat1 + $mat2) # function definition
mat3() # :(x + y)
mat1 = :z
mat3() # :(z + y)