User Tools

Site Tools


arraysintro


snippet.juliarepl
julia> pkgchk( [ "julia" => v"1.0.2" ] )

Arrays

First-class treatment of arrays (of floats) are the feature that turns general-purpose languages into scientific languages. Julia is first-class!

  • An array is an ordered mutable (i.e., read-write) collection of (same-type) elements.
  • Arrays can be of any type (e.g., an array of Bools, such as Array{Bool,1}, which is a vector of Booleans). However, in practice, arrays are most often used for numerical containers, such as Array{Float64,1} (a vector of Floats) or Array{Int,2} (a matrix of integers).
  • Avoid arrays of Any to keep the discipline and type-checking of narrow types. Ideally, use only arrays of machine-native types.
  • Vectors and matrices are aliases for one- and two-dimensional arrays:
snippet.juliarepl
julia> Array{Int64, 1} == Vector{Int64}
true

julia> Array{Int64, 2} == Matrix{Int64}
true

julia> Vector
Array{T,1} where T

julia> Matrix
Array{T,2} where T
  • Dot functions operate scalar functions on arrays on an element-by-element basis: abs.([-2,3,-2]) yields [2,3,2] in-situ.
  • WARNING: JULIA PASSES EFFECTIVELY REFERENCES
  • Functions never pass arrays, but references to arrays (“objects”). Thus, the function does not have copies of the vectors but aliases that refer to the same vector content.
  • Assignments are function calls, so even assignments do not make copies:
snippet.juliarepl
julia> a= [1,2];  ( b= a; ); ( b[1]= –99; b[2]= –98; );     a
2-element Array{Int64,1}:
 –9998
  • To make a copy of the contents in situ, you can use the .= operator:
snippet.juliarepl
julia> a= [1,2]; b= [0,0];    ( b.= a ); ( b[1]= –99; b[2]= –98; );    a
2-element Array{Int64,1}:
 1
 2
  • You can also use copy() or deepcopy() if you really need a copy and you do not want to clobber your original inadvertently.
  • WARNING: ACCIDENTAL TYPES AND OBJECTS DEFINITION MIXUPS
statement result explanation
v= Vector{Float64} a type desired? v is now an alias for a type. x= v([NaN]) creates a float vector variable
v= Vector{Float64}(undef,10) an uninitialized variable bad. v is a 10-element vector that holds garbage noise.
v= Vector{Float64}([10]) an initialized variable good. v is 1-element vector object.

A common beginner's mistake is some variant of v= Vector{Float64}; v + 0.0. A type and a value cannot be added.

Arrays are discussed in great detail in the next chapters.

  • In R, execution speed demands expressing every operation in vectors. This is because the R user program is interpreted, but its underlying libraries are compiled. Expressing operations in vectors is usually not helpful in Julia, because Julia compiles the user code, too. Thus, the fastest Julia operations are often based on plain old index-into-array manipulations—exactly the kind of operation that R suggests you to avoid.

Tuples

Tuples may be so intuitive enough that you may not need to read the remainder of this chapter.

  • A tuple is like a read-only array.
  • Tuples use parentheses notation () instead of array bracket notation [].
  • Think of a tuple as a value that can only appear on the right side of an assignment. Think of an array as an object that can appear on the left or on the right side of an assignment.
  • Any tuples are less harmful than Any arrays, because tuples are read-only and create less havoc in the compiler optimizations.
  • Tuples can also be considered to be immutable structs (albeit nameless); or as the arguments to a function sans the function name.
  • Tuples are well-suited to return many function return values. They can be typed like Tuple{Float64,Float64,Float64} or NTuple{3,Float64}

A tuple can pack elements, of any type, into a container. Tuples can hold specific values that are handed off as arguments to functions (or assignment to other data structures).

Creating Tuples

snippet.juliarepl
julia> ( ( 12, ("ab", true), 13.0 ) )       ## nested tuple --- outside-most parens are grouping, not tuple!
( 12, ("ab", true), 13.0 )

julia> x= ( ( 12, ("ab", true), 13.0 ) )    ## variable x now holds (read-only) tuple
(12, ("ab", true), 13.0)

julia> typeof(x)
Tuple{Int64,Tuple{String,Bool},Float64}

julia> dump(x)
Tuple{Int64,Tuple{String,Bool},Float64}
  1: Int64 12
  2: Tuple{String,Bool}
    1: String "ab"
    2: Bool true
  3: Float64 13.0

Single-Element Tuples

Because parenthesis also serve for grouping, single-element tuples require special notation:

snippet.juliarepl
julia> x= (1); typeof(x)      ## parens are groupings, so they group evaluations, and now you get a scalar, not a tuple!
Int64

julia> x= ((1)); typeof(x)    ## multiple parens are no better
Int64

julia> x= (1,); typeof(x)     ## if you need a single-element tuple, the trailing comma is the special case notation
Tuple{Int64}

julia> typeof( (1,2,) ) == typeof( (1,2) )    ## here the trailing comma is optional
true

WARNING parenthesis notation is also used to group statements.

Accessing Elements of Tuples

snippet.juliarepl
julia> ('a','b',3)[1]
'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase)

julia> x= ('a','b',3);  x[ [3,1,2,2] ]
(3, 'a', 'b', 'b')

julia> x[2]
'b': ASCII/Unicode U+0062 (category Ll: Letter, lowercase)

julia> x[2]='c'                           ## tuples are readonly!
ERROR: MethodError: no method matching setindex!(::Tuple{Char,Char,Int64}, ::Char, ::Int64)
Stacktrace:

Nesting and Merging Tuples

snippet.juliarepl
julia> t1= (:a,:b); t2= (:c,:d);	## define two tuples

julia> (t1,t2)				## creates a nested tuple
((:a, :b), (:c, :d))

julia> vcat( t1, t2 )			## an *array* of tuples
2-element Array{Tuple{Symbol,Symbol},1}:
 (:a, :b)
 (:c, :d)

julia> (t1...,t2...)			## a merged single tuple
(:a, :b, :c, :d)

Tuples as Function Arguments

The contents of an tuple can be unpacked for passing as the arguments of a function, too. This can work even using the ... operator. For example:

snippet.juliarepl
julia> f(x, y) = x + y
f (generic function with 1 method)

julia> t= (1, 2)
(1, 2)

julia> f(t...)
3

Tuples and Arrays

You may never need the information in this section.

Tuple-Array Conversions

Tuple to Array

To convert x from a tuple into an array

snippet.juliarepl
julia> tup3= ( (12,3), [1.0, 2.0], ["A", 'n', 13] )
((12, 3), [1.0, 2.0], Any["A", 'n', 13])

julia> typeof(tup3)
Tuple{Tuple{Int64,Int64},Array{Float64,1},Array{Any,1}}

julia> [ i for i in tup3 ]
3-element Array{Any,1}:
 (12, 3)
 [1.0, 2.0]
 Any["A", 'n', 13]

Julia could not know whether you meant

snippet.juliarepl
julia> ( (1,2), (3,4), (5,6) )
((1, 2), (3, 4), (5, 6))

to be a matrix or a 3-tuple of 2-tuples, so if you want to interpret it and convert it to a 3×2 matrix, then use the `reinterpret()` function.

Array to Tuple

snippet.juliarepl
julia> x= [ 1, 2, 3, "ab", true ]
5-element Array{Any,1}:
    1
    2
    3
     "ab"
 true

julia> tuple(x)                ## tuple with array inside, not a conversion
(Any[1, 2, 3, "ab", true],)

julia> tuple(x...)             ## tuple of elements from array
(1, 2, 3, "ab", true)

julia> Tuple(x)
(1, 2, 3, "ab", true)

The trailing comma on the tuple output helps to distinguish (1+2,) from (1+2)

Mixing Tuples and Arrays: Tuples of Tuples, Arrays of Tuples, and Tuples of Arrays

A tuple is not an array. A variable can hold either a tuple or an array.

snippet.juliarepl
julia> ( ( 12, ("ab", true), 13.0 ), ( 12, 13.0 ) )
((12, ("ab", true), 13.0), (12, 13.0))

julia> [ ( 12, ("ab", true), 13.0 ), ( 12, 13.0 ) ]
2-element Array{Tuple{Int64,Any,Vararg{Float64,N} where N},1}:
 (12, ("ab", true), 13.0)
 (12, 13.0)

For a mixed version

snippet.juliarepl
julia> x= ( (12,3), [1.0, 2.0], ["A", 'n', 13] )
((12, 3), [1.0, 2.0], Any["A", 'n', 13])

julia> typeof(x)
Tuple{Tuple{Int64,Int64},Array{Float64,1},Array{Any,1}}

You can now change x[2][2] (because it sits in an array), but not x[1][2] (because it sits in a tuple).

Convert Mixed Into Tuples or Arrays Only

snippet.juliarepl
julia> x= ( (12,3), [1.0, 2.0], ["A", 'n', 13, (3,4), [5,6]], )
((12, 3), [1.0, 2.0], Any["A", 'n', 13, (3,4), [5,6]])

FIXME How to descend into mixed tuple/array structure, and make all tuple or all array out of it.

snippet.juliafix
[download only julia statements]
julia> tupelize( x )
((12, 3), (1.0, 2.0), ("A", 'n', 13, (3,4), [5,6]))
 
julia> arraylize( x )
[[12, 3], [1.0, 2.0], Any["A", 'n', 13, [3,4], [5,6]]]

Backmatter

Notes

  • Unfortunately, it is not possible to turn on a compiler warning whenever an uninitialized variable is used before assignment.
  • There is also a more AbstractArray type. It is usually used as function arguments when the function should work not only on arrays but also on similar types (data structures like Diagonal). This chapter ignores this. AbstractArray{T,N} can also be useful, e.g., for sorting, but is too complex for this tutorial and thus ignored.
  • It is a pity that julia does not force type declarations for objects, but silently obliges to create them. This makes it easy to accidentally declare arrays with too broad a type, especially Any array types—which hoses both julia's type-checking and efficiency.
  • NamedTuples used to allow x= @NT( a=1, b=2 ), but are not working in Julia 1.0 in Sep 2018.

References

arraysintro.txt · Last modified: 2018/11/22 20:47 (external edit)