首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何根据任何splatted args的类型进行调度?

如何根据任何splatted args的类型进行调度?
EN

Stack Overflow用户
提问于 2020-08-06 22:19:23
回答 2查看 66关注 0票数 2

考虑Base中的一个现有函数,它接受一些抽象类型T的可变数量的参数。我已经定义了一个子类型S<:T,并且想要编写一个方法,如果有任何参数是我的子类型S,该方法就会调度它。

例如,考虑函数Base.cat,其中T是一个AbstractArrayS是一些MyCustomArray <: AbstractArray

所需的行为:

代码语言:javascript
运行
复制
julia> v = [1, 2, 3];

julia> cat(v, v, v, dims=2)
3×3 Array{Int64,2}:
 1  1  1
 2  2  2
 3  3  3

julia> w = MyCustomArray([1,2,3])

julia> cat(v, v, w, dims=2)
"do something fancy"

尝试:

代码语言:javascript
运行
复制
function Base.cat(w::MyCustomArray, a::AbstractArray...; dims)
    pritnln("do something fancy")
end

但这只在第一个参数为MyCustomArray时有效。

实现这一目标的优雅方法是什么?

EN

回答 2

Stack Overflow用户

发布于 2020-08-07 01:47:48

我想说,如果没有类型盗版,这是不可能做到的(但如果可能的话,我也想学习如何)。

例如,考虑您询问的cat。它在Base中有一个非常通用的签名(实际上在你写的时候不需要AAbstractArray ):

代码语言:javascript
运行
复制
julia> methods(cat)
# 1 method for generic function "cat":
[1] cat(A...; dims) in Base at abstractarray.jl:1654

你可以写一个特定的方法:

代码语言:javascript
运行
复制
Base.cat(A::AbstractArray...; dims) = ...

并检查A的任何元素是否是您的特殊数组,但这将是类型盗版。

现在的问题是,你甚至不能编写Union{S, T},因为S <: T将只作为T来解决。

这意味着您必须在签名中显式地使用S,但甚至:

代码语言:javascript
运行
复制
f(::S, ::T) = ...
f(::T, ::S) = ...

是有问题的,编译器会要求你定义f(::S, ::S),因为上面的定义会导致分派不明确。因此,即使您希望将可变参数的数量限制为某个最大数量,也必须将A的所有部分的类型注释为子集,以避免分派多义性(使用宏可以做到这一点,但会以指数级增加所需方法的数量)。

票数 2
EN

Stack Overflow用户

发布于 2020-08-07 16:04:23

对于一般用法,我同意Bogumił,但让我做一个额外的评论。如果您能够控制如何调用cat,那么您至少可以编写某种特征分派代码:

代码语言:javascript
运行
复制
struct MyCustomArray{T, N} <: AbstractArray{T, N}
    x::Array{T, N}
end

HasCustom() = Val(false)
HasCustom(::MyCustomArray, rest...) = Val(true)
HasCustom(::AbstractArray, rest...) = HasCustom(rest...)

# `IsCustom` or something would be more elegant, but `Val` is quicker for now
Base.cat(::Val{true}, args...; dims) = println("something fancy")
Base.cat(::Val{false}, args...; dims) = cat(args...; dims=dims)

而编译器足够酷,可以把它优化掉:

代码语言:javascript
运行
复制
julia> args = (v, v, w);

julia> @code_warntype cat(HasCustom(args...), args...; dims=2);
Variables
  #self#::Core.Compiler.Const(cat, false)
  #unused#::Core.Compiler.Const(Val{true}(), false)
  args::Tuple{Array{Int64,1},Array{Int64,1},MyCustomArray{Int64,1}}

Body::Nothing
1 ─ %1 = Main.println("something fancy")::Core.Compiler.Const(nothing, false)
└──      return %1

如果您无法控制对cat的调用,我能想到的使上述技术工作的唯一方法就是对包含此类调用的方法进行overdub,将匹配的调用替换为自定义实现。在这种情况下,你甚至不需要重载cat,但可以直接用一些mycat来代替它,做你喜欢做的事情。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63285604

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档