# 接口

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

## 迭代

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

`IteratorSize(IterType)``HasLength()``HasLength()``HasShape{N}()``IsInfinite()` ， 或者 `SizeUnknown()` 作为合适的
`IteratorEltype(IterType)``HasEltype()``EltypeUnknown()``HasEltype()` 都是可接受的
`eltype(IterType)``Any`元组中第一个条目的类型由 `iterate()` 返回。
`length(iter)`(未定义)条目数，如果已知
`size(iter, [dim...])`(未定义)在各个维度上条目的数量，如果知道
`IteratorSize(IterType)` 返回的值。必需方法
`HasLength()``length(iter)`
`HasShape{N}()``length(iter)``size(iter, [dim...])`
`IsInfinite()`(none)
`SizeUnknown()`(none)
`IteratorEltype(IterType)` 返回的值必需方法
`HasEltype()``eltype(IterType)`
`EltypeUnknown()`(none)

``````for i in iter   # or  "for i = iter"
# body
end``````

``````next = iterate(iter)
while next !== nothing
(i, 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 i in Squares(7)
println(i)
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 Array{Int64,1}:
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 Array{Int64,1}:
16
9
4
1``````

## 索引

`getindex(X, i)``X[i]`，索引元素访问
`setindex!(X, v, i)``X[i] = v`，索引元素赋值
`firstindex(X)`第一个索引
`lastindex(X)`最后一个索引，用于 `X[end]`

``````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 Array{Int64,1}:
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()`Returns either `IndexLinear()` or `IndexCartesian()`. See the description below.
`getindex(A, I...)`defined in terms of scalar `getindex`Multidimensional and nonscalar indexing
`setindex!(A, I...)`defined in terms of scalar `setindex!`Multidimensional and nonscalar indexed assignment
`iterate`defined in terms of scalar `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::NTuple{Int})``similar(A, eltype(A), dims)`返回具有相同元素类型和大小为 dims 的可变数组
`similar(A, ::Type{S}, dims::NTuple{Int})``Array{S}(undef, dims)`返回具有指定元素类型及大小的可变数组
`axes(A)``map(OneTo, size(A))`Return the `AbstractUnitRange` of valid indices
`Base.similar(A, ::Type{S}, inds::NTuple{Ind})``similar(A, S, Base.to_shape(inds))`Return a mutable array with the specified indices `inds` (see below)
`Base.similar(T::Union{Type,Function}, inds)``T(Base.to_shape(inds))`Return an array similar to `T` with the specified indices `inds` (see below)

``````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 Array{Int64,1}:
9
16

julia> s + s
4-element Array{Int64,1}:
2
8
18
32

julia> sin.(s)
4-element Array{Float64,1}:
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``````

If you are defining an array type that allows non-traditional indexing (indices that start at something other than 1), you should specialize `axes`. You should also specialize `similar` so that the `dims` argument (ordinarily a `Dims` size-tuple) can accept `AbstractUnitRange` objects, perhaps range-types `Ind` of your own design. For more information, see Arrays with custom indices.

## Strided Arrays

Methods to implementBrief description
`strides(A)`Return the distance in memory (in number of elements) between adjacent elements in each dimension as a tuple. If `A` is an `AbstractArray{T,0}`, this should return an empty tuple.
`Base.unsafe_convert(::Type{Ptr{T}}, A)`Return the native address of an array.
Optional methodsDefault definitionBrief description
`stride(A, i::Int)``strides(A)[i]`Return the distance in memory (in number of elements) between adjacent elements in dimension k.

A strided array is a subtype of `AbstractArray` whose entries are stored in memory with fixed strides. Provided the element type of the array is compatible with BLAS, a strided array can utilize BLAS and LAPACK routines for more efficient linear algebra routines. A typical example of a user-defined strided array is one that wraps a standard `Array` with additional structure.

Warning: do not implement these methods if the underlying storage is not actually strided, as it may lead to incorrect results or segmentation faults.

Here are some examples to demonstrate which type of arrays are strided and which are not:

``````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()`Broadcasting behavior of `SrcType`
`Base.similar(bc::Broadcasted{DestStyle}, ::Type{ElType})`Allocation of output container

`Base.BroadcastStyle(::Style1, ::Style2) = Style12()`Precedence rules for mixing styles
`Base.broadcast_axes(x)`Declaration of the indices of `x` for broadcasting purposes (defaults to `axes(x)`)
`Base.broadcastable(x)`Convert `x` to an object that has `axes` and supports indexing
Bypassing default machinery
`Base.copy(bc::Broadcasted{DestStyle})`Custom implementation of `broadcast`
`Base.copyto!(dest, bc::Broadcasted{DestStyle})`Custom implementation of `broadcast!`, specializing on `DestStyle`
`Base.copyto!(dest::DestType, bc::Broadcasted{Nothing})`Custom implementation of `broadcast!`, specializing on `DestType`
`Base.Broadcast.broadcasted(f, args...)`Override the default lazy behavior within a fused expression
`Base.Broadcast.instantiate(bc::Broadcasted{DestStyle})`Override the computation of the lazy broadcast's axes

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

### 广播风格

``````struct MyStyle <: Broadcast.BroadcastStyle end

• `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))``````

However, if needed you can specialize on any or all of these arguments. The final argument `bc` is a lazy representation of a (potentially fused) broadcast operation, a `Broadcasted` object. For these purposes, the most important fields of the wrapper are `f` and `args`, describing the function and argument list, respectively. Note that the argument list can — and often does — include other nested `Broadcasted` wrappers.

``````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(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``````

### Extending broadcast with custom implementations

In general, a broadcast operation is represented by a lazy `Broadcasted` container that holds onto the function to be applied alongside its arguments. Those arguments may themselves be more nested `Broadcasted` containers, forming a large expression tree to be evaluated. A nested tree of `Broadcasted` containers is directly constructed by the implicit dot syntax; `5 .+ 2.*x` is transiently represented by `Broadcasted(+, 5, Broadcasted(*, 2, x))`, for example. This is invisible to users as it is immediately realized through a call to `copy`, but it is this container that provides the basis for broadcast's extensibility for authors of custom types. The built-in broadcast machinery will then determine the result type and size based upon the arguments, allocate it, and then finally copy the realization of the `Broadcasted` object into it with a default `copyto!(::AbstractArray, ::Broadcasted)` method. The built-in fallback `broadcast` and `broadcast!` methods similarly construct a transient `Broadcasted` representation of the operation so they can follow the same codepath. This allows custom array implementations to provide their own `copyto!` specialization to customize and optimize broadcasting. This is again determined by the computed broadcast style. This is such an important part of the operation that it is stored as the first type parameter of the `Broadcasted` type, allowing for dispatch and specialization.

For some types, the machinery to "fuse" operations across nested levels of broadcasting is not available or could be done more efficiently incrementally. In such cases, you may need or want to evaluate `x .* (x .+ 1)` as if it had been written `broadcast(*, x, broadcast(+, x, 1))`, where the inner operation is evaluated before tackling the outer operation. This sort of eager operation is directly supported by a bit of indirection; instead of directly constructing `Broadcasted` objects, Julia lowers the fused expression `x .* (x .+ 1)` to `Broadcast.broadcasted(*, x, Broadcast.broadcasted(+, x, 1))`. Now, by default, `broadcasted` just calls the `Broadcasted` constructor to create the lazy representation of the fused expression tree, but you can choose to override it for a particular combination of function and arguments.

As an example, the builtin `AbstractRange` objects use this machinery to optimize pieces of broadcasted expressions that can be eagerly evaluated purely in terms of the start, step, and length (or stop) instead of computing every single element. Just like all the other machinery, `broadcasted` also computes and exposes the combined broadcast style of its arguments, so instead of specializing on `broadcasted(f, args...)`, you can specialize on `broadcasted(::DestStyle, f, args...)` for any combination of style, function, and arguments.

For example, the following definition supports the negation of ranges:

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

In-place broadcasting can be supported by defining the appropriate `copyto!(dest, bc::Broadcasted)` method. Because you might want to specialize either on `dest` or the specific subtype of `bc`, to avoid ambiguities between packages we recommend the following convention.

If you wish to specialize on a particular style `DestStyle`, define a method for

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

Optionally, with this form you can also specialize on the type of `dest`.

If instead you want to specialize on the destination type `DestType` without specializing on `DestStyle`, then you should define a method with the following signature:

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

This leverages a fallback implementation of `copyto!` that converts the wrapper into a `Broadcasted{Nothing}`. Consequently, specializing on `DestType` has lower precedence than methods that specialize on `DestStyle`.

Similarly, you can completely override out-of-place broadcasting with a `copy(::Broadcasted)` method.

#### Working with `Broadcasted` objects

In order to implement such a `copy` or `copyto!`, method, of course, you must work with the `Broadcasted` wrapper to compute each element. There are two main ways of doing so:

• `Broadcast.flatten` recomputes the potentially nested operation into a single function and flat list of arguments. You are responsible for implementing the broadcasting shape rules yourself, but this may be helpful in limited situations.
• Iterating over the `CartesianIndices` of the `axes(::Broadcasted)` and using indexing with the resulting `CartesianIndex` object to compute the result.

The precedence rules are defined by binary `BroadcastStyle` calls:

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

where `Style12` is the `BroadcastStyle` you want to choose for outputs involving arguments of `Style1` and `Style2`. For example,

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

indicates that `Tuple` "wins" over zero-dimensional arrays (the output container will be a tuple). It is worth noting that you do not need to (and should not) define both argument orders of this call; defining one is sufficient no matter what order the user supplies the arguments in.

For `AbstractArray` types, defining a `BroadcastStyle` supersedes the fallback choice, `Broadcast.DefaultArrayStyle`. `DefaultArrayStyle` and the abstract supertype, `AbstractArrayStyle`, store the dimensionality as a type parameter to support specialized array types that have fixed dimensionality requirements.

`DefaultArrayStyle` "loses" to any other `AbstractArrayStyle` that has been defined because of the following methods:

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

You do not need to write binary `BroadcastStyle` rules unless you want to establish precedence for two or more non-`DefaultArrayStyle` types.

If your array type does have fixed dimensionality requirements, then you should subtype `AbstractArrayStyle`. For example, the sparse array code has the following definitions:

``````struct SparseVecStyle <: Broadcast.AbstractArrayStyle{1} end
Whenever you subtype `AbstractArrayStyle`, you also need to define rules for combining dimensionalities, by creating a constructor for your style that takes a `Val(N)` argument. For example:
``````SparseVecStyle(::Val{0}) = SparseVecStyle()
These rules indicate that the combination of a `SparseVecStyle` with 0- or 1-dimensional arrays yields another `SparseVecStyle`, that its combination with a 2-dimensional array yields a `SparseMatStyle`, and anything of higher dimensionality falls back to the dense arbitrary-dimensional framework. These rules allow broadcasting to keep the sparse representation for operations that result in one or two dimensional outputs, but produce an `Array` for any other dimensionality.