Julia 朱莉娅:带内部构造函数的参数类型:new和typeof
试图了解参数类型和内部方法可用的Julia 朱莉娅:带内部构造函数的参数类型:new和typeof,julia,Julia,试图了解参数类型和内部方法可用的新函数。该手册声明“创建该类型新对象的内部构造函数可用的特殊函数”。请参阅手册中关于新建的章节和手册中关于内部构造函数方法的章节 考虑设计用于计算x之和的内部方法,其中x可以是向量或元组,并且被赋予参数类型T。自然需要的是x元素的类型由它们的总和s继承。我似乎不需要新的,对吗 struct M{T} x::T s function M(x) s = sum(x) x,s end end julia
新函数。该手册声明“创建该类型新对象的内部构造函数可用的特殊函数”。请参阅手册中关于新建的章节和手册中关于内部构造函数方法的章节
考虑设计用于计算x
之和的内部方法,其中x
可以是向量或元组,并且被赋予参数类型T
。自然需要的是x
元素的类型由它们的总和s
继承。我似乎不需要新的,对吗
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}
编辑:更正我打算让内部构造函数的最后一行是M(x,s)
。。。这仍然是一个有趣的问题,所以我不会纠正它。M(x,s)
与新的{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)
如果要将s
约束为与x
相同的类型,该怎么办?也就是说,例如,如果x
是一个向量,那么s
应该是一个向量(在这种情况下,是一个元素的向量)。我该怎么做?如果我用x,new{typeof(x)}(s)
替换内部构造函数的最后一行,我会得到可以理解的错误:
MethodError: Cannot `convert` an object of type Int64 to an object of type Vector{Int64}
记住,构造函数是用来构造某些东西的。具体而言,构造函数M
设计用于构造M
类型的值。您的示例构造函数
struct M{T}
x::T
s
function M(x)
s = sum(x)
x,s
end
end
表示对表达式M([1 2 3])
求值的结果是一个元组,而不是M
的实例。如果我在野外遇到这样一个构造函数,我会假设它是一个bug并报告它new
是一种内部魔法,允许您实际构造M
类型的值
这是一个抽象的问题。如果您首先想要一个元组,那么就不要考虑名为M
的结构,只需在模块范围内定义一个返回元组的函数M
。但是,如果您打算将其视为一种特殊的数据类型,可能用于动态分派,但甚至只是出于自我文档目的,那么构造函数应该返回类型为M
的值以下是规则:
如果您正在为类型M
编写外部构造函数,则构造函数应通过最终调用内部构造函数返回M
的实例,如下所示:M()
如果您正在编写内部构造函数,这将覆盖默认的内部构造函数。因此,必须通过调用new()
返回M
的实例
新的
“特殊函数”允许构造一个还没有构造函数的类型。观察以下示例:
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)
这是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
(上面B
的内部构造函数与默认内部构造函数完全相同。)
定义参数化类型时,必须为new
函数提供与类型参数数量相等的参数数量(且顺序相同),类似于参数化类型的默认内部构造函数。首先观察如何使用参数化类型的默认内部构造函数:
julia> struct Foo{T}
x::T
end
julia> Foo{String}("a")
Foo{String}("a")
现在,如果您正在为Foo
编写一个内部构造函数,而不是在构造函数内部编写Foo{T}(x)
,您将用new
替换Foo
,如下所示: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
我在这里使用的是typeof
,因此I
可以是任何返回数字的iterable类型:
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}
请注意,在参数化类型的内部构造函数中使用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
谢谢西尔维奥。我意识到我打算在内部构造函数中使用M(x,s)
!然而,你的答案非常有用,所以我不打算编辑这个问题。如果您能评论一下返回M(x,s)
和new{something}(x,s)
之间的区别,我将不胜感激。谢谢我相信,在你的情况下,没有太大的区别。如果您正在覆盖M(x,s)
(在这种情况下,您不能调用原始M(x,s)
,因为您刚刚替换了它)。您可以将每个内部构造函数视为隐式调用new
,除非您重写它以执行不同的操作。在您的例子中,您的M(x)
调用了由Julia运行时生成的M(x,s)
,并隐式调用了new(x,s)
。非常感谢您提供了这个超级详细的答案。这对我帮助很大!就在上面写着“我在这里使用typeof
,因此I
可以是任何返回数字的可编辑类型,typeof(s)
可以等价地替换为T
?如果我想要x::Float64
?我将{I,T}
更改为{T}
,抑制中的I
并写入新的{Float64,typeof(s)}(x,s)
,但这是一个