####################################################################
################# 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