定义尽可能少的方法以获得自定义对象的基本功能(Julia)

定义尽可能少的方法以获得自定义对象的基本功能(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

我仍然是Julia的初学者,所以很抱歉使用OOP术语

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