julia - 切片元组时避免类型不稳定
问题描述
这个问题是JuliaLang Zulip 帮助台中出现的问题的改编版本。
假设我有一个函数,该函数接收异构类型Tuple
并将返回该元组的一个切片,其中切片索引可以仅从类型信息中静态推断。如何以正确推断输出类型的方式编写函数?
例如,假设我的功能是
function f(t::Tuple, A::Array{T, N}) where {T, N}
if T <: AbstractFloat
imin = 1
elseif T <: Integer
imin = 2
else
imin = 3
end
imax = N+2
t[imin:imax]
end
我们看到类型推断只计算出这会产生 a Tuple
,而不是它的长度或元素类型,即使所有需要的信息在编译时都可用?
julia> let t = (:a, "b", 2, 3.0, Val(1), 2+im), A = rand(Int, 3,3)
Base.return_types(f, Tuple{typeof(t), typeof(A)})
end
1-element Array{Any,1}:
Tuple
我怎样才能写出f
这样的作品?
解决方案
我最喜欢的策略(但也许有更简单的方法?)是编写一个@generated
函数来手动确保 julia 在编译时执行我想要的类型级别操作:
@generated function f2(t::Tuple, A::Array{T, N}) where {T, N}
if T <: AbstractFloat
imin = 1
elseif T <: Integer
imin = 2
else
imin = 3
end
imax = N+2
out_expr = Expr(:tuple, (:(t[$i]) for i ∈ imin:imax)...)
end
这里的想法是,在生成的函数体中,在编译时,我们确定什么imin
和imax
是,然后我们手动为函数体构建一个表达式,读取为(t[imin], t[imin+1], ..., t[imax-1], t[imax])
.
无论出于何种原因,julia 能够更好地推理序列而getindex(::Tuple, ::Int)
不是切片元组,即使是静态已知切片,所以通过手动构建这个表达式,编译器能够做我们想做的事情:
julia> let t = (:a, "b", 2, 3.0, Val(1), 2+im), A = rand(Int, 3,3)
Base.return_types(f2, Tuple{typeof(t), typeof(A)})
end
1-element Array{Any,1}:
Tuple{String,Int64,Float64}
瞧,推断的输出类型是Tuple
长度为 3 的 a,其元素静态已知为 a String
,Int
和Float64
!
推荐阅读
- ios - 在 NativeScript 中访问 iOS 通用链接中的查询参数
- javascript - Google Earth Engine - 连接“年份”以迭代函数
- postgresql - postgresql 中的 copy 和 \copy 命令之间的区别
- linux - 在linux中分配文件而不归零并且不创建稀疏文件
- python - 如何在不破坏遗留代码的情况下增加函数的返回值?
- python - 以 HDF5 格式存储字符串数据集时出现 ValueError
- api - Airtable - 使用 vue js 格式化“多选”字段
- wikipedia-api - Mediawiki API:如何列出一个类别的所有子类别?
- nginx - 502 badgateway nginx ALB 将 http 重定向到 https
- python - 在 Python 中搜索多个 SQlite db 以查找列集中的特定字符串的有效方法?