目前我有如下代码:
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 个赞