Julia:带有内部构造函数的参数类型:new 和 typeof

Pat*_*ckT 6 julia

尝试了解参数类型和new内部方法可用的函数。该手册指出“可用于创建该类型的新对象的内部构造函数的特殊函数”。请参阅此处有关new 的手册部分和此处有关内部构造函数方法的手册部分。

考虑一个设计用于计算 之和的内部方法x,其中x可以是向量或元组,并给出参数类型T。自然而然地希望 的元素类型x能够通过它们的 sum 继承s。我似乎不需要new这个,对吗?

struct M{T}
    x::T
    s
    function M(x)
        s = sum(x)
        x,s
    end
end


julia> M([1,2,3])
([1, 2, 3], 6)

julia> M([1.,2.,3.])
([1.0, 2.0, 3.0], 6.0)

julia> typeof(M([1.,2.,3.]))
Tuple{Vector{Float64}, Float64}
Run Code Online (Sandbox Code Playgroud)

编辑: 更正!我打算让内部构造函数的最后一行是M(x,s)......这仍然是一个有趣的问题,所以我不会纠正它。与有何M(x,s)不同new{typeof(x)}(x,s)

我见过的一种用法new是与 结合使用typeof(),例如:

struct M{T}
    x::T
    s
    function M(x)
        s = sum(x)
        new{typeof(x)}(x,s)
    end
end

julia> M([1,2,3])
M{Vector{Int64}}([1, 2, 3], 6)

julia> M([1.,2.,3.])
M{Vector{Float64}}([1.0, 2.0, 3.0], 6.0)
Run Code Online (Sandbox Code Playgroud)

如果想限制s为与 相同的类型怎么办x?也就是说,例如,如果x是一个向量,那么s应该是一个向量(在这种情况下,是一个元素的向量)。我该怎么做呢?如果我用 替换内部构造函数的最后一行x, new{typeof(x)}(s),我会得到可以理解的错误:

MethodError: Cannot `convert` an object of type Int64 to an object of type Vector{Int64}
Run Code Online (Sandbox Code Playgroud)

Cam*_*nek 2

规则如下:

  1. 如果您正在为类型编写外部构造函数M,则该构造函数应通过最终调用内部构造函数返回一个实例M,如下所示M(<args>)

  2. 如果您正在编写内部构造函数,这将覆盖默认的内部构造函数。M因此,您必须通过调用返回一个实例new(<args>)

“特殊函数new”的存在是为了允许构造还没有构造函数的类型。观察以下示例:

julia> struct A
           x::Int
           function A(x)
               A(x)
           end
       end

julia> A(4)
ERROR: StackOverflowError:
Stacktrace:
 [1] A(::Int64) at ./REPL[3]:4 (repeats 79984 times)
Run Code Online (Sandbox Code Playgroud)

这是 的构造函数的循环定义A,会导致堆栈溢出。你无法自力更生,因此 Julia 提供了该new函数作为规避此问题的方法。

您应该为new函数提供的参数数量等于结构中的字段数量。请注意,该new函数将尝试转换其输入的类型以匹配结构体字段的声明类型:

julia> struct B
           x::Float64
           B(x) = new(x)
       end

julia> B(5)
B(5.0)

julia> B('a')
B(97.0)

julia> B("a")
ERROR: MethodError: Cannot `convert` an object of type String to an object
of type Float64
Run Code Online (Sandbox Code Playgroud)

(上面的内部构造函数B与默认内部构造函数完全相同。)

定义参数类型时,new必须为函数提供与您的类型的参数数量相同的参数(并且顺序相同),类似于参数类型的默认内部构造函数。首先观察参数类型的默认内部构造函数是如何使用的:

julia> struct Foo{T}
           x::T
       end

julia> Foo{String}("a")
Foo{String}("a")
Run Code Online (Sandbox Code Playgroud)

现在,如果您正在为 编写内部构造函数Foo,而不是Foo{T}(x)在构造函数内部编写,您可以将 替换Foonew,如下所示:new{T}(x)

您可能需要typeof帮助定义构造函数,但通常不需要。这是定义M类型的一种方法:

struct M{I, T}
    x::I
    s::T

    function M(x::I) where I
        s = sum(x)
        new{I, typeof(s)}(x, s)
    end
end
Run Code Online (Sandbox Code Playgroud)

typeof在这里使用的I是任何返回数字的可迭代类型:

julia> typeof(M(1:3))
M{UnitRange{Int64},Int64}

julia> g = (rand() for _ in 1:10)
Base.Generator{UnitRange{Int64},var"#5#6"}(var"#5#6"(), 1:10)

julia> typeof(M(g))
M{Base.Generator{UnitRange{Int64},var"#5#6"},Float64}
Run Code Online (Sandbox Code Playgroud)

请注意,当您在参数类型的内部构造函数中使用时,需要new为您的类型提供参数:

julia> struct C{T}
           x::Int
           C(x) = new(x)
       end
ERROR: syntax: too few type parameters specified in "new{...}" around REPL[6]:1
Run Code Online (Sandbox Code Playgroud)