【问题标题】:Creating Arrays from Ranges in Julia without using Collect()在不使用 Collect() 的情况下在 Julia 中从范围创建数组
【发布时间】:2019-08-21 14:48:11
【问题描述】:

在从范围创建数组时,我对 Julia 中的这种行为感到有些困惑。我想知道以下的基本机制。

A = [1:10]

1-element Array{UnitRange{Int64},1} 中的结果

这不是我想要的。上面的代码创建了一个 of UnitRange 的数组。 Julia 文档建议使用 collect() 从范围创建数组,如下所示:

A = collect(1:10)

结果为@​​987654326@。完美。

但是,如果我在范围后添加分号,此代码也可以工作。

A = [1:10;]

根据 Julia 文档,;vcat() 或垂直连接的简写。 vcat 用作A = [1:10;] 有什么意义。它不仅看起来很奇怪(它是用来做什么的?),而且对我来说没有意义。

我希望能清楚地解释范围如何与 vcat 交互。

【问题讨论】:

    标签: julia


    【解决方案1】:

    ranges 是从不分配的“惰性”向量。它可能是最有用的迭代器之一。

    julia> AbstractRange <: AbstractVector
    true
    
    julia> @allocated [1,2,3,4,5,6,7,8,9,10]
    160
    
    julia> @allocated 1:10
    0
    

    范围运算符: 用于创建范围:

    julia> 1:10 |> dump
    UnitRange{Int64}
      start: Int64 1
      stop: Int64 10
    

    您已经知道如何使用 collect 将范围转换为向量,但如果您可以更深入地研究代码,您会发现 collect 实际上在后台调用了 vcat

    julia> @less collect(1:10)
    
    collect(r::AbstractRange) = vcat(r) 
    

    这就是vcat 处理AbstractRange 输入的方式:

    @less vcat(1:10)    
    
    function vcat(rs::AbstractRange{T}...) where T
        n::Int = 0
        for ra in rs
            n += length(ra)
        end
        a = Vector{T}(undef, n)
        i = 1
        for ra in rs, x in ra
            @inbounds a[i] = x
            i += 1
        end
        return a
    end
    

    实现非常简单,只需循环输入(注意rs 是可变参数输入),并将输入范围一个接一个地连接到一个向量中。显然,即使只有一个输入范围([1:10;])也能正常工作。

    还有另一种从范围创建向量的方法:直接调用Vector 构造函数Vector(1:10)。但是引擎盖下会发生什么?简单地调用@less Vector(1:10) 不会直接跳转到原始实现,这就是花哨的调试器的用武之地:

    julia> using Debugger
    
    julia> @enter Vector(1:10)
    In Type(x) at boot.jl:424
    >424  (::Type{Array{T,N} where T})(x::AbstractArray{S,N}) where {S,N} = Array{S,N}(x)
    
    About to run: (Core.apply_type)(Array, Int64, 1)
    1|debug> s
    In Type(x) at boot.jl:424
    >424  (::Type{Array{T,N} where T})(x::AbstractArray{S,N}) where {S,N} = Array{S,N}(x)
    
    About to run: (Array{Int64,1})(1:10)
    1|debug> s
    [ Info: tracking Base
    In Type(r) at range.jl:943
    >943  Array{T,1}(r::AbstractRange{T}) where {T} = vcat(r)
    
    About to run: (vcat)(1:10)
    1|debug> s
    In vcat(rs) at range.jl:930
    >930  n::Int = 0
     931  for ra in rs
     932      n += length(ra)
     933  end
     934  a = Vector{T}(undef, n)
    
    About to run: Core.NewvarNode(:(_5))
    

    如您所见,Vector 也调用了vcat

    我认为这个示例已经为您提供了一些关于如何使用这些非常方便的内置反射工具以交互方式在 Julia REPL 中找到答案的想法。还有其他有用的工具,例如@code_lowered@code_typed@macroexpand 等,可以帮助您找出诸如“这个表达式有什么作用?”之类的问题,例如,

    julia> f() = [1:10;]
    f (generic function with 1 method)
    
    julia> @code_lowered f()
    CodeInfo(
    1 ─ %1 = 1:10
    │   %2 = (Base.vcat)(%1)
    └──      return %2
    )
    

    “降低”的代码告诉我们,Julia 首先创建一个范围%1 = 1:10,然后调用Base.vcat(%1),这正是文档所说的。

    X-ref:What is the difference between @code_native, @code_typed and @code_llvm in Julia?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-10-28
      • 2018-08-23
      • 2013-07-01
      • 1970-01-01
      • 2020-10-30
      • 1970-01-01
      • 2018-03-18
      • 2016-10-07
      相关资源
      最近更新 更多