引用不带点符号的结构字段(在Julia中)

时间:2018-07-15 13:40:06

标签: julia

在Julia中,我定义了一个类型,我需要编写一些可用于该类型字段的函数。有些函数包含复杂的公式,并且在各处使用字段访问点表示法会很混乱。因此,我最终将字段值放入局部变量中以提高可读性。效果很好,但是有什么聪明的方法可以避免必须键入所有a=foo.a行或让Julia将a解析为foo.a等?

struct Foo
    a::Real
    b::Real
    c::Real
end

# this gets hard to read
function bar(foo::Foo)
    foo.a + foo.b + foo.c + foo.a*foo.b - foo.b*foo.c
end

# this is better
function bar(foo::Foo)
    a = foo.a
    b = foo.b
    c = foo.c
    a + b + c + a*b - b*c
end

# this would be great
function bar(foo::Foo)
    something clever
    a + b + c + a*b - b*c
end

2 个答案:

答案 0 :(得分:6)

由于Julia通常鼓励使用通用接口与字段进行交互,而不是直接访问字段,因此,实现此目标的相当自然的方法是通过迭代解包。在Julia中,可以通过迭代将对象“拆包”为多个变量:

julia> x, y = [1, 2, 3]
3-element Array{Int64,1}:
 1
 2
 3

julia> x
1

julia> y
2

我们可以为诸如Foo之类的自定义对象实现这种迭代协议。在v0.7中,它看起来像:

Base.iterate(foo::Foo, state = 1) = state > 3 ? nothing : (getfield(foo, state), state + 1)

请注意,3是硬编码的(基于Foo中的字段数),可以用fieldcount(Foo)代替。现在,您只需按以下步骤“解压缩” Foo的实例即可:

julia> a, b, c = Foo("one", 2.0, 3)
Foo("one", 2.0, 3)

julia> a
"one"

julia> b
2.0

julia> c
3

这可能是功能开始时的“聪明之处”。此外,从v0.7开始,您可以在函数参数本身中解压缩字段:

function bar((a, b, c)::Foo)
    a + b + c + a*b - b*c
end

尽管这确实需要您再次提及字段名称,但它具有两个潜在的优点:

  1. 在重构您的struct并重命名字段的情况下,所有访问字段的代码将保持不变(只要字段顺序不变或iterate实现被更改反映新对象的内部结构)。
  2. 更长的字段名称可以缩写。 (即,您可以选择使用apples,而不是使用完整的a字段名称。)

如果不要重复字段名称很重要,则可以定义一个宏以生成所需的变量(a = foo.a; b = foo.b; c = foo.c);但是,这可能会使您的代码读者更加困惑,并且缺少上面列出的优点。

答案 1 :(得分:2)

从 Julia 1.6 开始,这个包中的宏看起来很相关:https://github.com/mauro3/UnPack.jl

语法如下:

function bar(foo::Foo)
    # something clever!
    @unpack a, b, c = f

    a + b + c + a*b - b*c
end

在 Julia 1.7 中,看起来这个特性将与语法一起添加

function bar(foo::Foo)
    # something clever!
    (; a, b, c) = f

    a + b + c + a*b - b*c
end

这是合并的拉取请求:https://github.com/JuliaLang/julia/pull/39285