首页 > 解决方案 > Julia 中推断的类型差异

问题描述

我正在尝试学习来自 Python 的 Julia,我在exercism.io上遇到了一段有趣的代码。用户做了一个优雅的技巧来创建包含函数的元组,因为它们是 Julia 中的一等对象。以此为基础,我想尝试一些东西。

假设我有一个列表:

my_list = zip(0:3, ["wink", "double blink", "close your eyes", "jump"]) |> collect

我想创建一个由 2 元素元组组成的列表,其中第二个元素是一个函数:

codes = [(i, x -> push!(x,j)) for (i,j) in my_list]
append!(codes, (4, reverse!))

代码无法运行。检查 REPL 中的签名,我意识到上面的第一行生成了一个带有签名的列表:4-element Array{Tuple{Int64,var"#68#70"{String}},1}:

而如果我像链接代码中那样手动执行该过程:

    codes = 
    [ (0, i -> push!(i, "wink"))
    , (1, i -> push!(i, "double blink"))
    , (2, i -> push!(i, "close your eyes"))
    , (3, i -> push!(i, "jump"))
    , (4, reverse!)]

我得到了正确的类型:5-element Array{Tuple{Int64,Function},1}. 我无法理解差异以及为什么我尝试做的不是有效代码。

标签: julia

解决方案


首先请注意,您不应该push!append!向量末尾添加一个元素(append!将集合的元素附加到另一个集合)。现在我将集中讨论主要问题,假设您会push!在代码中使用。

的所有元素code具有相同的类型:

julia> typeof.(codes)
4-element Array{DataType,1}:
 Tuple{Int64,var"#4#6"{String}}
 Tuple{Int64,var"#4#6"{String}}
 Tuple{Int64,var"#4#6"{String}}
 Tuple{Int64,var"#4#6"{String}}

julia> unique(typeof.(codes))
1-element Array{DataType,1}:
 Tuple{Int64,var"#4#6"{String}}

甚至更多 - 这种类型是具体的:

julia> isconcretetype.(typeof.(codes))
4-element BitArray{1}:
 1
 1
 1
 1

(这意味着事情将变得稳定且快速,这很好)

在这种情况下,理解将这种类型设置为eltype结果向量。

问题是(4, reverse!)元组有不同的类型:

julia> typeof((4, reverse!))
Tuple{Int64,typeof(reverse!)}

所以你不能将它添加到codes向量中,即:

julia> push!(codes, (4, reverse!))
ERROR: MethodError: Cannot `convert` an object of type typeof(reverse!) to an object of type var"#4#6"{String}

现在怎么解决?像这样创建它时设置一个适当eltype的向量:codes

julia> codes = Tuple{Int, Function}[(i, x -> push!(x,j)) for (i,j) in my_list]
4-element Array{Tuple{Int64,Function},1}:
 (0, var"#7#8"{String}("wink"))
 (1, var"#7#8"{String}("double blink"))
 (2, var"#7#8"{String}("close your eyes"))
 (3, var"#7#8"{String}("jump"))

julia> push!(codes, (4, reverse!))
5-element Array{Tuple{Int64,Function},1}:
 (0, var"#7#8"{String}("wink"))
 (1, var"#7#8"{String}("double blink"))
 (2, var"#7#8"{String}("close your eyes"))
 (3, var"#7#8"{String}("jump"))
 (4, reverse!)

一切都会按预期工作。

让我举一个同样问题的更简单的例子,让问题更清晰可见:

julia> x = [i for i in 1:3]
3-element Array{Int64,1}:
 1
 2
 3

julia> eltype(x)
Int64

julia> push!(x, 1.5)
ERROR: InexactError: Int64(1.5)
Stacktrace:
 [1] Int64 at ./float.jl:710 [inlined]
 [2] convert at ./number.jl:7 [inlined]
 [3] push!(::Array{Int64,1}, ::Float64) at ./array.jl:913
 [4] top-level scope at REPL[55]:1

julia> x = Float64[i for i in 1:3]
3-element Array{Float64,1}:
 1.0
 2.0
 3.0

julia> push!(x, 1.5)
4-element Array{Float64,1}:
 1.0
 2.0
 3.0
 1.5

并且append!会像这样工作(继续上一个示例):

julia> append!(x, [2.5, 3.5])
6-element Array{Float64,1}:
 1.0
 2.0
 3.0
 1.5
 2.5
 3.5

推荐阅读