# 接口

Julia 的很多能力和扩展性都来自于一些非正式的接口。通过为自定义的类型扩展一些特定的方法，自定义类型的对象不但获得那些方法的功能，而且也能够用于其它的基于那些行为而定义的通用方法中。

## 迭代

iterate(iter)通常返回由第一项及其初始状态组成的元组，但如果为空，则返回 nothing
iterate(iter, state)通常返回由下一项及其状态组成的元组，或者在没有下一项存在时返回 nothing

IteratorSize(IterType)HasLength()HasLength()HasShape{N}()IsInfinite() 或者 SizeUnknown() 中合适的一个
IteratorEltype(IterType)HasEltype()EltypeUnknown()HasEltype() 中合适的一个
eltype(IterType)Anyiterate() 返回元组中第一项的类型。
length(iter)(未定义)项数，如果已知
size(iter, [dim])(未定义)在各个维度上项数，如果已知
IteratorSize(IterType) 返回的值必需方法
HasLength()length(iter)
HasShape{N}()length(iter)size(iter, [dim])
IsInfinite()()
SizeUnknown()()
IteratorEltype(IterType) 返回的值必需方法
HasEltype()eltype(IterType)
EltypeUnknown()(none)

for item in iter   # or  "for item = iter"
# body
end

next = iterate(iter)
while next !== nothing
(item, state) = next
# body
next = iterate(iter, state)
end

julia> struct Squares
count::Int
end

julia> Base.iterate(S::Squares, state=1) = state > S.count ? nothing : (state*state, state+1)

julia> for item in Squares(7)
println(item)
end
1
4
9
16
25
36
49

julia> 25 in Squares(10)
true

julia> using Statistics

julia> mean(Squares(100))
3383.5

julia> std(Squares(100))
3024.355854282583

julia> Base.eltype(::Type{Squares}) = Int # Note that this is defined for the type

julia> Base.length(S::Squares) = S.count

julia> collect(Squares(4))
4-element Vector{Int64}:
1
4
9
16

julia> Base.sum(S::Squares) = (n = S.count; return n*(n+1)*(2n+1)÷6)

julia> sum(Squares(1803))
1955361914

julia> Base.iterate(rS::Iterators.Reverse{Squares}, state=rS.itr.count) = state < 1 ? nothing : (state*state, state-1)

julia> collect(Iterators.reverse(Squares(4)))
4-element Vector{Int64}:
16
9
4
1

## Indexing

Methods to implementBrief description
getindex(X, i)X[i], indexed element access
setindex!(X, v, i)X[i] = v, indexed assignment
firstindex(X)The first index, used in X[begin]
lastindex(X)The last index, used in X[end]

For the Squares iterable above, we can easily compute the ith element of the sequence by squaring it. We can expose this as an indexing expression S[i]. To opt into this behavior, Squares simply needs to define getindex:

julia> function Base.getindex(S::Squares, i::Int)
1 <= i <= S.count || throw(BoundsError(S, i))
return i*i
end

julia> Squares(100)[23]
529

julia> Base.firstindex(S::Squares) = 1

julia> Base.lastindex(S::Squares) = length(S)

julia> Squares(23)[end]
529

julia> Base.getindex(S::Squares, i::Number) = S[convert(Int, i)]

julia> Base.getindex(S::Squares, I) = [S[i] for i in I]

julia> Squares(10)[[3,4.,5]]
3-element Vector{Int64}:
9
16
25

## 抽象数组

size(A)返回包含 A 各维度大小的元组
getindex(A, i::Int)（若为 IndexLinear）线性标量索引
getindex(A, I::Vararg{Int, N})（若为 IndexCartesian，其中 N = ndims(A)）N 维标量索引
setindex!(A, v, i::Int)（若为 IndexLinear）线性索引元素赋值
setindex!(A, v, I::Vararg{Int, N})（若为 IndexCartesian，其中 N = ndims(A)）N 维标量索引元素赋值

IndexStyle(::Type)IndexCartesian()返回 IndexLinear()IndexCartesian()。请参阅下文描述。
getindex(A, I...)基于标量 getindex 定义多维非标量索引
setindex!(A, X, I...)基于标量 setindex! 定义多维非标量索引元素赋值
iterate基于标量 getindex 定义Iteration
length(A)prod(size(A))元素数
similar(A)similar(A, eltype(A), size(A))返回具有相同形状和元素类型的可变数组
similar(A, ::Type{S})similar(A, S, size(A))返回具有相同形状和指定元素类型的可变数组
similar(A, dims::Dims)similar(A, eltype(A), dims)返回具有相同元素类型和大小为 dims 的可变数组
similar(A, ::Type{S}, dims::Dims)Array{S}(undef, dims)返回具有指定元素类型及大小的可变数组

axes(A)map(OneTo, size(A))返回有效索引的 AbstractUnitRange{<:Integer}
similar(A, ::Type{S}, inds)similar(A, S, Base.to_shape(inds))返回使用特殊索引 inds 的可变数组（详见下文）
similar(T::Union{Type,Function}, inds)T(Base.to_shape(inds))返回类似于 T 的使用特殊索引 inds 的数组（详见下文）

julia> struct SquaresVector <: AbstractArray{Int, 1}
count::Int
end

julia> Base.size(S::SquaresVector) = (S.count,)

julia> Base.IndexStyle(::Type{<:SquaresVector}) = IndexLinear()

julia> Base.getindex(S::SquaresVector, i::Int) = i*i

julia> s = SquaresVector(4)
4-element SquaresVector:
1
4
9
16

julia> s[s .> 8]
2-element Vector{Int64}:
9
16

julia> s + s
4-element Vector{Int64}:
2
8
18
32

julia> sin.(s)
4-element Vector{Float64}:
0.8414709848078965
-0.7568024953079282
0.4121184852417566
-0.2879033166650653

julia> struct SparseArray{T,N} <: AbstractArray{T,N}
data::Dict{NTuple{N,Int}, T}
dims::NTuple{N,Int}
end

julia> SparseArray(::Type{T}, dims::Int...) where {T} = SparseArray(T, dims);

julia> SparseArray(::Type{T}, dims::NTuple{N,Int}) where {T,N} = SparseArray{T,N}(Dict{NTuple{N,Int}, T}(), dims);

julia> Base.size(A::SparseArray) = A.dims

julia> Base.similar(A::SparseArray, ::Type{T}, dims::Dims) where {T} = SparseArray(T, dims)

julia> Base.getindex(A::SparseArray{T,N}, I::Vararg{Int,N}) where {T,N} = get(A.data, I, zero(T))

julia> Base.setindex!(A::SparseArray{T,N}, v, I::Vararg{Int,N}) where {T,N} = (A.data[I] = v)

julia> A = SparseArray(Float64, 3, 3)
3×3 SparseArray{Float64, 2}:
0.0  0.0  0.0
0.0  0.0  0.0
0.0  0.0  0.0

julia> fill!(A, 2)
3×3 SparseArray{Float64, 2}:
2.0  2.0  2.0
2.0  2.0  2.0
2.0  2.0  2.0

julia> A[:] = 1:length(A); A
3×3 SparseArray{Float64, 2}:
1.0  4.0  7.0
2.0  5.0  8.0
3.0  6.0  9.0

julia> A[1:2,:]
2×3 SparseArray{Float64, 2}:
1.0  4.0  7.0
2.0  5.0  8.0

julia> copy(A)
3×3 SparseArray{Float64,2}:
1.0  4.0  7.0
2.0  5.0  8.0
3.0  6.0  9.0

julia> A[SquaresVector(3)]
3-element SparseArray{Float64, 1}:
1.0
4.0
9.0

julia> sum(A)
45.0

## 等步长数组

strides(A)返回每个维度中相邻元素之间的内存距离（以内存元素数量的形式）组成的元组。如果 AAbstractArray{T,0}，这应该返回空元组。
Base.unsafe_convert(::Type{Ptr{T}}, A)返回数组的本地内存地址
Base.elsize(::Type{<:A})返回数组中连续元素的步长

stride(A, i::Int)strides(A)[i]返回维度 i（译注：原文为 k）上相邻元素之间的内存距离（以内存元素数量的形式）。

1:5   # not strided (there is no storage associated with this array.)
Vector(1:5)  # is strided with strides (1,)
A = [1 5; 2 6; 3 7; 4 8]  # is strided with strides (1,4)
V = view(A, 1:2, :)   # is strided with strides (1,4)
V = view(A, 1:2:3, 1:2)   # is strided with strides (2,4)
V = view(A, [1,2,4], :)   # is not strided, as the spacing between rows is not fixed.

## 自定义广播

Base.BroadcastStyle(::Type{SrcType}) = SrcStyle()SrcType 的广播行为
Base.similar(bc::Broadcasted{DestStyle}, ::Type{ElType})输出容器的分配

Base.BroadcastStyle(::Style1, ::Style2) = Style12()混合广播风格的优先级规则
Base.axes(x)用于广播的 x 的索引的声明（默认为 axes(x)
Base.broadcastable(x)x 转换为一个具有 axes 且支持索引的对象

Base.copy(bc::Broadcasted{DestStyle})broadcast 的自定义实现
Base.copyto!(dest, bc::Broadcasted{DestStyle})专门针对 DestStyle 的自定义 broadcast! 实现
Base.copyto!(dest::DestType, bc::Broadcasted{Nothing})专门针对 DestStyle 的自定义 broadcast! 实现
Base.Broadcast.broadcasted(f, args...)覆盖融合表达式中的默认惰性行为
Base.Broadcast.instantiate(bc::Broadcasted{DestStyle})覆盖惰性广播的 axes 的计算

• 确保所有参数都支持广播
• 为给定参数集选择合适的输出数组
• 为给定参数集选择高效的实现

### 广播风格

struct MyStyle <: Broadcast.BroadcastStyle end
Base.BroadcastStyle(::Type{<:MyType}) = MyStyle()

• Base.BroadcastStyle(::Type{<:MyType}) = Broadcast.Style{MyType}() 可用于任意类型。
• 如果 MyType 是一个 AbstractArray，首选是 Base.BroadcastStyle(::Type{<:MyType}) = Broadcast.ArrayStyle{MyType}()
• 对于只支持某个具体维度的 AbstractArrays，请创建 Broadcast.AbstractArrayStyle{N} 的子类型（请参阅下文）。

### 选择合适的输出数组

Base.similar(bc::Broadcasted{DestStyle}, ::Type{ElType})

similar(bc::Broadcasted{DefaultArrayStyle{N}}, ::Type{ElType}) where {N,ElType} =
similar(Array{ElType}, axes(bc))

struct ArrayAndChar{T,N} <: AbstractArray{T,N}
data::Array{T,N}
char::Char
end
Base.size(A::ArrayAndChar) = size(A.data)
Base.getindex(A::ArrayAndChar{T,N}, inds::Vararg{Int,N}) where {T,N} = A.data[inds...]
Base.setindex!(A::ArrayAndChar{T,N}, val, inds::Vararg{Int,N}) where {T,N} = A.data[inds...] = val
Base.showarg(io::IO, A::ArrayAndChar, toplevel) = print(io, typeof(A), " with char '", A.char, "'")

Base.BroadcastStyle(::Type{<:ArrayAndChar}) = Broadcast.ArrayStyle{ArrayAndChar}()

function Base.similar(bc::Broadcast.Broadcasted{Broadcast.ArrayStyle{ArrayAndChar}}, ::Type{ElType}) where ElType
# Scan the inputs for the ArrayAndChar:
A = find_aac(bc)
# Use the char field of A to create the output
ArrayAndChar(similar(Array{ElType}, axes(bc)), A.char)
end

"A = find_aac(As) returns the first ArrayAndChar among the arguments."
find_aac(args::Tuple) = find_aac(find_aac(args[1]), Base.tail(args))
find_aac(x) = x
find_aac(::Tuple{}) = nothing
find_aac(a::ArrayAndChar, rest) = a
find_aac(::Any, rest) = find_aac(rest)

julia> a = ArrayAndChar([1 2; 3 4], 'x')
2×2 ArrayAndChar{Int64, 2} with char 'x':
1  2
3  4

julia> a .+ 1
2×2 ArrayAndChar{Int64, 2} with char 'x':
2  3
4  5

julia> a .+ [5,10]
2×2 ArrayAndChar{Int64, 2} with char 'x':
6   7
13  14

### 使用自定义实现扩展广播

broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::OrdinalRange) = range(-first(r), step=-step(r), length=length(r))

### 扩展 in-place 广播

In-place 广播可通过定义合适的 copyto!(dest, bc::Broadcasted) 方法来支持。由于你可能想要专门化 destbc 的特定子类型，为了避免包之间的歧义，我们建议采用以下约定。

copyto!(dest, bc::Broadcasted{DestStyle})

copyto!(dest::DestType, bc::Broadcasted{Nothing})

#### 使用 Broadcasted 对象

• Broadcast.flatten 将可能的嵌套操作重新计算为单个函数并平铺参数列表。你自己负责实现广播形状规则，但这在有限的情况下可能会有所帮助。
• 迭代 axes(::Broadcasted)CartesianIndices 并使用所生成的 CartesianIndex 对象的索引来计算结果。

### 编写二元广播规则

Base.BroadcastStyle(::Style1, ::Style2) = Style12()

Base.BroadcastStyle(::Broadcast.Style{Tuple}, ::Broadcast.AbstractArrayStyle{0}) = Broadcast.Style{Tuple}()

BroadcastStyle(a::AbstractArrayStyle{Any}, ::DefaultArrayStyle) = a
BroadcastStyle(a::AbstractArrayStyle{N}, ::DefaultArrayStyle{N}) where N = a
typeof(a)(Val(max(M, N)))

struct SparseVecStyle <: Broadcast.AbstractArrayStyle{1} end
Base.BroadcastStyle(::Type{<:SparseMatrixCSC}) = SparseMatStyle()
SparseVecStyle(::Val{0}) = SparseVecStyle()
SparseVecStyle(::Val{N}) where N = Broadcast.DefaultArrayStyle{N}()