定义尽可能少的方法以获得自定义对象的基本功能(Julia)
我仍然是Julia的初学者,所以很抱歉使用OOP术语 设定义尽可能少的方法以获得自定义对象的基本功能(Julia),julia,custom-object,Julia,Custom Object,我仍然是Julia的初学者,所以很抱歉使用OOP术语 设X和Y为两个用户定义的矩阵,元素类型为MyType。设I为单位矩阵MyType恰好是一个具有单个属性的对象,所有操作都是通过该属性完成的。 我试图测试((X-I))^-1是否近似于Y。我希望通过定义尽可能少的函数来实现这一点。但我也不想MyType继承任何东西 我希望我只需要定义: 身份矩阵的1和0。I的元素应该知道它们属于类型MyType +(x::MyType,y::MyType),-,*,/用于矩阵乘法和求逆 伴随用于确定(X-I
X
和Y
为两个用户定义的矩阵,元素类型为MyType
。设I
为单位矩阵<在本例中,code>MyType恰好是一个具有单个属性的对象,所有操作都是通过该属性完成的。
我试图测试((X-I))^-1
是否近似于Y
。我希望通过定义尽可能少的函数来实现这一点。但我也不想MyType
继承任何东西
我希望我只需要定义:
- 身份矩阵的
和1
。0
的元素应该知道它们属于类型I
MyType
,+(x::MyType,y::MyType)
,-
,*
用于矩阵乘法和求逆/
用于确定伴随
(X-I)
给出isapprox
≈代码>某种意义
这是我到目前为止所拥有的。我花了一些时间才最终停止获取
MethodError
s
# Define MyType
struct MyType # Don't inherit from anything
value
end
value(x::MyType) = x.value
# Define its methods
Base.one(::Type{MyType}) = MyType(1)
Base.zero(::Type{MyType}) = MyType(0)
Base.:+(x::MyType, y::MyType) = MyType(value(x)+value(y))
Base.:*(x::MyType, y::MyType) = MyType(value(x)*value(y))
Base.:-(x::MyType, y::MyType) = MyType(value(x)-value(y))
Base.:/(x::MyType, y::MyType) = MyType(value(x)/value(y))
Base.adjoint(x::MyType) = MyType(adjoint(value(x)))
function Base.isapprox(x::MyType, y::MyType; atol, rtol)
return isapprox(value(x), value(y), atol=atol, rtol=rtol)
end
function Base.rtoldefault(::Type{MyType}, ::Type{MyType}, z)
# I don't really want to be restricted to Float64 tolerance.
return Base.rtoldefault(Float64, Float64, z)
end
# Shouldn't have to define these
Base.one(::MyType) = one(MyType)
Base.zero(::MyType) = zero(MyType)
Base.:+(x::MyType, y) = MyType(value(x)+y)
Base.:*(x::MyType, y) = MyType(value(x)*y)
Base.abs(x::MyType) = MyType(abs(value(x)))
Base.:<(x::MyType, y::MyType) = value(x) < value(y)
Base.inv(x::MyType) = MyType(inv(value(x)))
Base.promote_rule(::Type{Any}, ::Type{MyType}) = MyType
Base.convert(::Type{MyType}, x::MyType) = x
Base.convert(::Type{MyType}, x) = MyType(x)
Base.iterate(x::MyType) = (value(x), nothing)
Base.iterate(::MyType, ::Any) = nothing
Base.length(x::MyType) = 1
# Begin check for ((X-I)')^-1 ≈ Y
X = [
0.4 1;
-0.2 0.8;
]
X = map(x -> MyType(x), X)
Y = [
-0.625 0.625;
-3.125 -1.875;
]
Y = map(x -> MyType(x), Y)
using LinearAlgebra
println((X-I)')
println(inv((X-I)'))
println(inv((X-I)') ≈ Y)
#定义MyType
struct MyType#不从任何内容继承
价值
结束
值(x::MyType)=x.value
#定义其方法
Base.one(::Type{MyType})=MyType(1)
Base.zero(::Type{MyType})=MyType(0)
基本类型::+(x::MyType,y::MyType)=MyType(值(x)+值(y))
基本类型:*(x::MyType,y::MyType)=MyType(值(x)*值(y))
基::-(x::MyType,y::MyType)=MyType(值(x)-值(y))
基本类型:/(x::MyType,y::MyType)=MyType(值(x)/值(y))
基本伴随(x::MyType)=MyType(伴随(值(x)))
函数Base.isapprox(x::MyType,y::MyType;atol,rtol)
返回isapprox(值(x),值(y),atol=atol,rtol=rtol)
结束
函数Base.rtoldefault(::Type{MyType},::Type{MyType},z)
#我真的不想被限制在容忍范围之内。
返回Base.rtoldefault(Float64,Float64,z)
结束
#不应该定义这些
Base.one(::MyType)=one(MyType)
Base.zero(::MyType)=zero(MyType)
基数::+(x::MyType,y)=MyType(值(x)+y)
基数:*(x::MyType,y)=MyType(值(x)*y)
Base.abs(x::MyType)=MyType(abs(值(x)))
Base.:缺少回退方法有时是一种遗漏(您可以提交一个请求来解决这个问题),有时是故意的。让我们以zero
为例:为什么没有回退zero(x)=zero(typeof(x))
,这样你就不必自己写了?显然,这不难补充,有人可能会说我们应该拥有它。但是考虑一下,如果用户创建了一个新类型,然后需要一个未定义的方法:会发生什么?
julia> struct MyMatrix{T}
buffer::Vector{T}
sz::Tuple{Int,Int}
end
julia> M = MyMatrix(rand(9), (3, 3));
julia> zero(M)
ERROR: MethodError: no method matching zero(::MyMatrix{Float64})
Closest candidates are:
zero(::Union{Type{P}, P}) where P<:Dates.Period at /home/tim/src/julia-master/usr/share/julia/stdlib/v1.7/Dates/src/periods.jl:53
zero(::T) where T<:Dates.TimeType at /home/tim/src/julia-master/usr/share/julia/stdlib/v1.7/Dates/src/types.jl:423
zero(::SparseArrays.AbstractSparseArray) at /home/tim/src/julia-master/usr/share/julia/stdlib/v1.7/SparseArrays/src/SparseArrays.jl:55
...
Stacktrace:
[1] top-level scope
@ REPL[3]:1
julia>struct MyMatrix{T}
缓冲区::向量{T}
sz::元组{Int,Int}
结束
julia>M=MyMatrix(rand(9)、(3,3));
朱莉娅>零(米)
错误:MethodError:没有与零匹配的方法(::MyMatrix{Float64})
最接近的候选人是:
零(::Union{Type{P},P})其中PBy“not inheriting”,您的意思是不希望它成为任何类型的子类型吗(例如,Real
或Number
?@BenoitPasquier是的。它应该只是Any
的一个子类型。你介意我问一下为什么吗?看起来你希望它像Number
一样,但不是一个。虽然这是完全有效的,但我很好奇你为什么想要:)@BenoitPasquier它主要用于用户体验。我制作了一个库,目前可用于Rational
,Complex
,等等,我想将它扩展到几乎任何类型。我已经使用了数组类型,但我仍然想让它用于用户定义的类型。然后我可以告诉用户需要实现什么方法您可以使用Base.one(::Union{Type{MyType},MyType})=MyType(1)
和same表示零,并添加类似mytypematrix(x::Matrix)=[MyType(value)表示x中的值]
为了避免每次都要映射一个矩阵,谢谢你的解释。因此,我要确认的是,共识是要么我应该像Number
那样进行子类型划分,要么我应该使用额外的方法?我同意这个设计决定,并且我在数组{Any,2}
方面也面临着类似的难题。我决定将设为零(::Type{Array{T,2}}),其中T=LinearAlgebra.UniformScaling(0)
。这非常有效。但是zeros
抱怨zero
的返回类型不是Array{T,2}
。zero
的返回类型比较宽松,但zeros
不宽松,这有什么设计原因吗?同样,在类似的问题上,zero([12;34])
可以工作,但是zeros([12;34],2)
不能。这当然也可以改进吗?
julia> Base.zero(M::MyMatrix) = zero(typeof(M))
julia> zero(M)
ERROR: MethodError: no method matching zero(::Type{MyMatrix{Float64}})
Closest candidates are:
zero(::Union{Type{P}, P}) where P<:Dates.Period at /home/tim/src/julia-master/usr/share/julia/stdlib/v1.7/Dates/src/periods.jl:53
zero(::MyMatrix) at REPL[4]:1
zero(::T) where T<:Dates.TimeType at /home/tim/src/julia-master/usr/share/julia/stdlib/v1.7/Dates/src/types.jl:423
...
Stacktrace:
[1] zero(M::MyMatrix{Float64})
@ Main ./REPL[4]:1
[2] top-level scope
@ REPL[5]:1