#################################################################### ################# Let's learn Julia quick and easy.################# #################################################################### ### Run, Shell, Debug, Navigate % julia # run Julia as command line REPL client % julia file.jl [arg1 [arg2 [..]]] # run file.jl as a script in the shell. #!/...path/to/julia # first line of a julia script # Atom editor + Juno IDE, nice for Julia. julia> Open Terminal # opens shell from inside Juno. println(), print() # use to get arbitrary debug info versioninfo() # build details. varinfo() # global vars and their types. ^D exit. ^C interrupt computation. ^L clear screen. ...; suppress output. ? enter help mode. ; enter shell mode. ] enter package mode. ] mode commands: add pkgname, st(atus) up(date) pkgname, rm pkgname pwd(), ls() # print working directory cd("D:\") # change local directory. include("f.jl") # read and run a julia file like a script exit() exit(1) # 0 (Unix success) is default exit code. GC.gc() # run garbage collector (rarely) clipboard(a) # copy a to system clipboard clipboard() # return clipboard contents as a string. ### Julia proper. # comment #= multi line comment =# ## Variables: case-sensitive unicode letter-initial names # (number prefix interprets as multiply: 6x == 6*x == x*6 != x6) # \unicode-name gets you that unicode character. \alpha, \euler, pi # Variables are typeless; their values are what have types. Supplied to # a function, the runtime picks the most specifically typed matching method # defined for that function, and uses that. ## (Scalar) Types e.g. T: Int64, Float64, Irrational, Char, String, Bool. # Types are part of function-calling, for generality: Op{TypeList}(args) # Types may be explicit (declared) or implicit (undeclared) # Type declaration: a::B # a must be of type B a<:B # a must be of a subtype of B # Math: + - * / == !== === > < >= <= # as expected. +(1,2,3) also works. muladd(a,b,c)==a*b+c, minmax([1,2,3])==(1,3), 0x12 hex uint. 0b11 binary uint. 3.8 Float64. 4 + 3im == Complex(4,3). typemin(T), typemax(T), eps(T), precision(FloatType) Math utilities (with r::Float=1.0, c::Complex=3+2i) round(r), ceil(r), floor(r), trunc(r) (toward 0.0), clamp(r,lo,hi) (in [lo,hi]) real(c)==3, imag(c)==2, reim(c)==(3,2), conj(c)==3-2i, angle(c)(in radians) cis(r)==exp(i*r), sign(r)==+-1, mod2pi(c) == c % 2\pi, modf(r)==(0.8,3) lcm(a,b) # least common multiple of a & b gcd(a,b) # greatest (positive) common denominator. // for division of Rational #'s. + is 190x overloaded. true == 1 != true && false == 1 != false # evaluation is not symmetric. const a = 1 # const means you can change a's value but not a's type. res = convert(T,a) # raises error e.g. InexactError if conversion is lossy or fails string(2017) # string from number parse(Int64,"2017") # number from string parse.(T,[a,b,c]) # parse an array into type T. This is "Broadcasting" ## Stringops: split(s,'\t'), join([s1,s2],"\t"), replace(s,"from" => "to") s1 * s2 * s3 # concatenate, == string(s1,s2,s3) "$var in string" # interpolates. ## Structs abstract type Plant end # default is a concrete type, instantiable abstract type Weed <: Plant end # abstract types can be in hierarchies. # struct defines a concrete type. mutable struct Pea <: Weed {T<:Number} # default is immutable after object creation member # no type required on members/properties member2::T # Explicit or generalized property types ok. end pea = Pea("in good soil",10) # initializer arg order == struct definition order. a = pea.member2 = 12; # assign away, it's mutable. superType(T) subType(T) isa(obj,T) # as expected. typeof(obj) fieldNames(T) eltype(array) # as expected # Object methods don't exist. Use functions that can handle your object's type too. # Property inheritance doesn't exist, because parent abstract types lack properties. # Inheritance is from abstract types. Also multiple inheritance is not supported. # Instead *include* structs as explicit members of other structs. ### Array (types) ## 1D: VECTORS a = Int64[]; # zero length array. == Array{T,1}() == Vector{T}(); a = zeros(n); # n length array of zeroes(|ones(),) == zeros(Int64,5) a = [1;2;3] # column vector (; or , represent rowsep, ; suppresses output) a = [1 2 3] # row "vector" (Matrix of 1 row, n cols. cols are primitive) a = [10,"foo",false"] # array of type Any (slow) a = Union{Int64,String,Bool}[10,"foo",false"] # limit types for speed a = Array(T,1}(undef,n) # row vector of n elements, initial values garbage. a[1] # first element (not zero-based but one-based index counting) a[from:step:to] # slice iterator from from to to by step step. # collect(0:0.01:1) # convert iterator to list. # a[1;10:12;100] == a[1;10;11;12;100] == vcat[1;10:12;100] a[end:-1:1] # reverse the order of elements length(a) # return array length in(el,a) # returns Bool whether el is in a isempty(a) # returns Bool whether a is empty findall(BoolF,a) # BoolF (e.g. x -> x==value) returns array of Bools, # findall converts to list of indexes. !: op(arg) returns the transformed arg, but op!(arg) also modifies the original of arg, in place. push!(a,b) # insert b at end of array a pushfirst!(a,b) # insert b at start of array a pop!(a) # remove element from end of array a popfirst!(a) # remove element from start of array a append!(a,b) # insert element b or array b's elements to end of array a vcat(1,a,b) # new array concatenating element 1, elements of a & of b deleteat!(a,pos)# remove the element at pos from array a sort!(a) # sort in place. sort(a) # don't modify the original shuffle!(a) # pre-call "using Random". Shuffle (in place) m(ax|in)imum(a) # return m(ax|in)imum of array a empty!(a) # replace column vector a with an empty vector: ?length 0? ## 2D: MATRICES a = [[1,2,3] [4,5,6]] # two columns each having three rows. == [[1 4]; [2 5]; [3 6] # three rows, each having two columns == vcat([1 4],[2 5],[3 6]) # vcat concatenates vectors into a matrix. ndims(a) # 2 for a matrix, 1 for a vector size(a) # show the dimensions (max index value for each dimension) row first. # a 1-dimensional array is a (column) vector, indexed by rowN's. a = [[1,2,3],[4,5,6] # vector of 2 vectors each of 3 elements. a[2][2]==5 # access into array nesting with [][] a = Array{T}(undef,0,0,0,0) # empty 4D array of Ts. # arrays are stored contiguously in the first dimension, so loop faster thus # for (col=loC:hiC) for (row=loR:hiR) { ... } # inner loop contiguous. a = [[1,2,3] [4,5,6]]; mask=[[true,true,false] [false,true,false] a[mask] == [1,2,5] # extract array elements with a mask array (flattening) reshape(a,d1,d2) # tells a to have d1 rows, d2 cols, still contiguously # stored as before (contents of a column contiguous) dropdims(a,..) a'==transpose(a) # transpose (numerical) matrix a. [1,2,3] != collect([1 2 3]') # [1,2,3] is a column vector # collect([1 2 3]') is an array with 3 rows, 1 column. AbstractVector{T} == AbstractArray{T,1} # an alias AbstractMatrix{T} == AbstractArray{T,2} # an alias a = [100x+10y+z for x in 1:2, y in 3:4, z in 5:6] # list comprehension => n-D array # Matrix tools: *(A,B) # matrix multiply A and B. == A*B. dot(a,b) # dot product vectors a and b. == a\cdotb (or matrices columnwise) eigvals(), eigvecs() factorize() # might factorize as LU, QR, SVD, Eigen, LQ, Schur, &c &c. Thus A = factorize(A); x=A\b; y=A\c; # efficiency! reused! lufact() # L U factorization. inv() # invert a matrix A\B # Matrix division. Solves for x in Ax=B. # If A & B square, returns inv(A)*B # various special forms are detected and handled efficiently. # A symmetric: a[i,j] == a[j,i] # A Hermitian: a[i,j] == conj(a[j,i]) (has real eigenvalues) # A upper or lower triangular or diagonal, no factorization needed, # so forward or backward substitution solves it. pinv() # pseudo inverse istril() # is a lower triangular matrix. ## Tuples a = (1,2,3) == 1,2,3 # a tuple not an array x,y = (1,2) # unpacking, esp for multiple returns from functions ## NamedTuples (like Dictionaries but immutable, type-stable, more efficient) nt = (a=1, b=2) nt.a == 1 == nt[:a] keys[nt] == (a,b) # a tuple values[nt] == (1,2) # a tuple collect(nt) == [1,2] for (key,val in pairs(nt) [...] end # pairs(a)converts tuple to iterable ## Dictionaries (subtly different from NamedTuples: mutable, type unstable) d = Dict('a'=>1, 'b'=>2) # create dict d['c']=3 # add to dict value by name d.a == 1 == d[:a] map( (i,j) -> d[i]=j, [1,2,3], [10,20,30]) # then d=('1'=>10,'2'=>20,'3'=>30) d['nonexistent'] # get a value, raise an error on miss. keys(d) # an iterator (collect() to make an Array) values(d) # an iterator haskey(d,'a') # return Bool in(('a' => 1), d) # return Bool for (k,v in d) println("$k => $v") end ## Sets a = Set() # empty set a = Set([1,2,3,4]) # unordered unique values intersect|union|setdiff(s1,s2) # the usual ## Assignment (and copying) deepcopy(x) copies everything recursively to a new memory object copy(x) deepcopies everything but containers within containers a=b deepcopies only simple types, containers get a reference. == Same values === Same bits (if immutable) or same memory address (if mutable) ## Random numbers rand() # random float in [0,1] rand(a:b) # random int in [a,b] rand(a:0.01:b) # random float in [0,1] with precision 0.01. == using Pkg; Pkg.add("Distributions"); import Distributions: Uniform rand(Uniform(a,b)) rand(Uniform(a,b),2,3) # 2x3 matrix of floats in the range [a,b]. ## Logic: && || ! for AND OR and NOT false && op() # Lazy eval means op() is never evaluated because never reached. & | ~ xor >> << Bitwise logic & shift. >>> is unsigned right bit shift. isa(a,T), isnumeric(a), iseven(a), isodd(a), ## 0: nothing, missing, NaN nothing (type Nothing) == #NULL returned to raise an error missing (type Missing) == gap in a data matrix, handle with good stats methods NaN (type Float64) = 0/0, not a number. Inf (1/0), -Inf (-1/0) ## Control Flow: for while if/else do break continue: as expected. () optional. for outer=1:2, inner=30:10:40 # multiple conditions ok. println(outer+inner); end a ? b : c # if a true then b else c. Needs the spaces. # Error handling try .. catch error("error message") end # works as expected. cf isa(), rethrow(). # map & list comprehensions [func(i) for i in [1,2,3]] # make an array with the 3 outputs [r+c for r in [10,20,30], c in [1,2,3]] # 2D array as map or list comprehension [mydict[i]=val for (i,val) in enumerate(myTuple)] # enumerate returns index & el [students[name]=grade for (name,grade) in zip(names,grades)] # zip cols into 2tuples map((n,g) -> students[n]=g, names,grades) # the mapped function takes (n,g) and # puts them into the students object map(f,[1,2,3]) == map(x->f(x), [1,2,3]) # alias for single-arg function f. map(+, [1,2,3], [10,20,30], [100,200,300]) # -> [111,222,333] findall(array) do x; x == 3; end # Do Blocks: shove it in where a # function is needed as first argument to findall (or another outer # function). ## Functions f(x,y) = 2x+y # define function inline function f(x) # define function using keyword 'function' x+2 # return x+2 is optional. Last computed value is returned anyway. # x, x+2 # could return a tuple. end a = f # you can assign a reference to a function, then use it b = a(12) f!(changeme,leaveme) = changeme = changeme+leaveme # ! means 1st arg is modified x -> x+1 # anonymous function, no name, take x as arg, return result. f = (x,y) -> x*y # anonymous functions can take tuples as arguments, can be assigned. function f(a,b,c=1;d,e=2) .. end # positional args then ; then named args. # within that, args w/o default value before args w/ default value. # Multi-type args: f(x::Union{Float64,Vector{Float64}}=Float64[]) [...] end # x is one or more Float64s # Ellipses a.k.a. argument list function shmu(init,args...) s=0 for arg in args s += arg end return init+s/length(args) end shmu(10,1,2,3) # works shmu(10,[1,2,3] ...) # works ## Multiple "dispatch" # not well explained. Seems related to args being unions of various types, # where the function call picks from the allowed types at compile time. okay. # "type-safe" not defined. # methods(f) enumerates the type combinations allowed for function f. f.(a) i.e., broadcast(f,a) # apply f to each element of a. ## Function parameter type declaration: f(x::T, y::T2, z::T2) where {T <: Number, T2} = 5x+5y+5z # (y & z need to be same type. # maybe that's not explicative T for any type so you know but code-explicit T # meaning here are new types call them T and T2 & specify T a subset of Number. ## recursive self-coding. y = :X # do not evaluate X. # == quote X end eval(y) # evaluate it now. a = 1; y = :($a+2) # == 1+2, $-evaluation happens inside :-quoted expressions Meta.parse("1+2") == :(1+2)) == Expr(:call, :+, 1, 2) # Expr has :head and :args array :b == Symbol("b"), a thing for later evaluation, to use a new or later value then. macro unless(test_expr, branch_expr) # macros allow language definition. quote # a quoted expression is the value of the macro if !$test_expr # evaluate $expr's when macro is called to supply values. $branch_expr # then unless test_expr evals true, branch_expr evals end end # Later this macro can be used to modify Julia itself. @unless a b # call with @macroName # evidently you can access Julia's source code within Julia and modify # it with these tools. ## I/O open("out.txt","w") do f write(f,"Hi World\n") end # close(f) is implicit in do block open( "in.txt","r") do f s = read(f,String) print(s) end # reads entire file open( "in.txt","r") do f for ln in eachline(f) println(f) end end # reads entire file # for (i,ln) in enumerate(eachline(f)) println(i,f) end # prints line numbers too. # Useful packages CSV.jl, HTTP.jl, OdsIO.js(OpenDocument spreadsheets), HDF5.jl?? # RCall, ## Data using Pkg; Pkg.add("Plots") ... using DelimitedFiles txtMatrix = readdlm(inf,'\t')[2:end,1:end] # ignore line 1 (col labels) floatMatrix = convert(Array{Float64,2},txtMatrix) using DataFrames # as in R using CSV path = "/../path/to/file.csv"; df = CSV.read(path,DataFrame); first(df,5) # print first 5 rows of data frame df. using Plots, Random Random.seed!(1) # reproducible x, y = 1:100, randn(100); plot(x,y) # some plots scatter(x,y) histogram(y) bar(y) Use also packages Distributions, StatsPlots ## Other # currying # closures