目前我有如下代码:
const allowedaxes = (:first, :second, :third, :fourth, :fifth, :sixth)
function CartesianCoordinates(t::Vararg{T, N}) where {N, T <: Real}
NamedTuple{tuple(allowedaxes[1:N]...), NTuple{N, T}}((t))
end
应该如何用 NamedTuple
为基础实现一个自己的类型呢?
如果像上面实现的,写一个 wrapper 函数,就直接把 NamedTuple
这个实现暴露给用户了,而且写函数参数类型的时候就要写 a(x::NamedTuple, b::NamedTuple)
会导致不必要的匹配;但是如果自己写一个类型 CartesianCoordinates
就要多写好多方法,把 NamedTuple
的方法们 嫁接过来;有没有两全其美的办法呢?
Jun
2018 年10 月 6 日 07:17
2
类似的问题我也遇到过,之前搜了下,似乎没找到特别好的解决办法。
如果我没理解错的话,你的CartesianCoordinates
其实是一类带约束的NamedTuple
。可以这么做:
构造一个新的类型CartesianCoordinates
,将NamedTuple
作为其成员,在构造函数里做检查。struct CartesianCoordinates{T<:NamedTuple}
np::T
CartesianCoordinates(t::Vararg{T, N}) where {N, T <: Real} = new{NamedTuple{tuple(allowedaxes[1:N]...), NTuple{N, T}}((t))
end
实现NamedTuple
的所有方法。这个我没有实际写过(我之前的case只需要支持有限的几个方法,类似这样实现的 ),不过感觉应该没有你说的那么复杂。
2.1 通过methodswith
函数获取所有NamedTuple
的方法
2.2 通过f.sig
获取各个方法的signature,将其中的NamedTuple
替换成CartesianCoordinates
2.3 执行@eval
,将所有x::CartesianCoordinates
映射到x.np
如果你写出来了,不妨分享交流下~
1 个赞
Roger
2018 年10 月 13 日 17:23
3
如果是笛卡尔坐标,我倾向于这样写
struct CartCoord{N, T}
positions::NTuple{N, T}
end
不过,如果是别的需要用 NamedTuple
,你可以:
struct Foo{NT <: NamedTuple}
x::NT
end
using MacroTools: @forward
@forward Foo.x Base.length # blablabla
其实这件事情比较适合通过传递一个interface完成,不过目前还没啥成熟的interface解决方案,很多package就直接用那个forward宏,比起自己重载能少写一写代码
2 个赞
Thanks! 话说是不是 @forward Foo.x Base.length # blablabla
? 多打了一个 l
.
对于Base.length这种来说@forward
是足够用的,另一种情况是需要重新定义一下运算规则,手写一个for循环即可
for op in [:(==), :≈, :<]
@eval Base.$op(x::TrackedReal, y::Real) = Base.$op(data(x), y)
@eval Base.$op(x::Real, y::TrackedReal) = Base.$op(x, data(y))
@eval Base.$op(x::TrackedReal, y::TrackedReal) = Base.$op(data(x), data(y))
end
参考Flux
1 个赞