Julia is a just-in-time compiled (very, very
similar to interpreted to me) programming language that attempts to
provide ease of programming with near C computational speed,
especially for numerical tasks. It can infer types (or accept static
types), deal with Unicode, support parallel code execution, be run in
a shell like an interpreted language, and interface with compiled code
from other languages, mainly C. It has garbage collection that can be
triggered manually (gc()
).
The file suffix for julia code is .jl
.
-
Indexing starts at 1 and not 0. Hmmm.
-
Single and double quotes mean different things ('c’har vs. "string").
Obtaining
The Julia download page is a GitHub thing. It has a "Download ZIP" button, but this is not recommended. It makes an inconsistent tree of Julia. A better way is…
git clone git://github.com/JuliaLang/julia
Then…
cd julia
make
Takes forever.
Running Code
Just executing the julia
command will run an "interpreter" that
compiles and executes each line as it is entered.
Code can be compiled and run directly:
julia -e 'help()'
I find this kind of ominous:
$ time julia-v0.1.2 -e 'quit()'
real 0m9.611s
Yikes. Looks like this is being worked on. Indeed, here is a hint of things moving in the right direction:
$ time julia-v0.3.0-prerelease+1406 -e 'quit()'
real 0m1.582s
Compare to Python 2.7.4:
$ time python -c 'raise SystemExit'
real 0m0.052s
If you’re going to compile it from source, start with installing
gfortran
, m4
, libncurses5-dev
, and possibly others.
Then git clone git://github.com/JuliaLang/julia.git
and make
.
Style
Comments use #
.
Variables
Variables…
-
…must start with
[a-zA-Z_]
. Underscore is discouraged. -
…are case sensitive. Normally lower case (functions and macros too).
-
…do not imply type, etc.
-
…can contain unicode messes.
-
…can overwrite many important things (
pi=3.1
). -
…can not overwrite reserved statements (
if=4
).
Oddly functions which modify their input have names that end in !
.
These are "in-place" functions. I guess like sort!
vs. sort
, where
the former returns the list sorted and the latter returns a new copied
sorted list with the original unsorted.
Using the const
keyword makes that variable into a constant. This
improves performance where possible.
const TOLERABLE_ERROR = 0.000001
Types
There is a typeof()
function if you’re unsure. Also typemin()
and
typemax()
are handy to find out the numerical range limits of
various types.
Int8 |
[-128,127] |
|
Int16 |
[-32768,32767] |
|
Int32 |
[-2147483648,2147483647] |
|
Int64 |
[-9223372036854775808,9223372036854775807] |
|
Int128 |
[-170141183460469231731687303715884105728,170141183460469231731687303715884105727] |
|
Uint8 |
[0x00,0xff] |
|
Uint16 |
[0x0000,0xffff] |
|
Uint32 |
[0x00000000,0xffffffff] |
|
Uint64 |
[0x0000000000000000,0xffffffffffffffff] |
|
Uint128 |
[0x00000000000000000000000000000000,0xffffffffffffffffffffffffffffffff] |
There is a BigInt
type for arbitrary precision work (internally uses
GNU’s GMP). Otherwise overflow will wrap around modolo like.
Here’s an example of BigInt
in action:
@printf("%.70f",1/BigFloat("9998"))
0.0001000200040008001467614281998486092106759315356612205505371093750000
Note
|
This is not exactly the fascinating answer the answer that http://wolframalpha.com gives. Hmm. Try it. |
Numbers with decimal points are Float64
unless told otherwise with a
definition like 0.5f0
in which case it’s a Float32
. Hex works with
something like 0x1p0
(internally a Float64
).
For weird math fans, there is a positive and a negative 0
representation. They are ==
. There is also positive and negative
inf
and an unsigned nan
. Full weirdness tools!
Concerned about binary to float conversion weirdness? Check out machine epsilon.
There is a function one(t)
and a function zero(t)
where t
is
type. This ensures you get a one or a zero that is exactly the kind
you want.
You can cast things to be a specific type with the ::
operator.
Something like:
julia> typeof(3::Int64)
Int64
When attached to a variable, the ::
declarations constrain the
variable to that type like a statically-typed language, hopefully with
all the performance benefits.
There is a bitstype
type which allows you to define your own
type which is a collection of arbitrary bits.
There is a composite type feature that produces effects like C’s
struct
.
julia> type Cd
name
seconds::Int
end
julia> cd1= Cd("Lennon",133)
Cd("Lennon",133)
Instead of type
you can use the key word immutable
which will give
you a structure that can not change once it’s instantiated. Or
something like that.
Eval
Julia supports an eval
command since program code is stored in the
same data structures the code uses. This allows for "templates". My
favorite application of this in Bash is to print out some code that
will be run to see if it’s acceptable, and if so, run it.
Operators
To me, the defining feature of Julia’s syntax is the ability to omit
*
when writing many multiplication idioms. For example, 3x
,
(x-1)x
, 2(x+3)^2
, etc. However, (x+3)(x-1)
, apparently is not
cool. Avoid things that look like functions, e.g. x(y+3)
. Use
cautiously.
All the normal operators are present. "Not" is !
for bool and ~
for bits. I found $
interesting for xor. Updating operators like
+=
work fine.
Some special weird math functions are isfinite()
, isinf()
, and
isnan()
. Also isequal()
is the more exacting friend of ==
.
Boolean types can be considered 1
(true) and 0
(false) in
operations like x > true
. This allows the bool producing operators
to be chained.
Math Functions
-
round() iround()
-
floor() ifloor()
-
ceil() iceil
-
trunc() itrunc()
-
div(x,y) - Truncated division, like Python
5/2
. -
fld(x,y) - huh?
-
rem(x,y) - remainder, different from mod in sign matching.
-
mod(x,y) - Python’s %
-
gcd(x,y,z,…) - Greatest common denominator.
-
lcm(x,y,z,…) - Least common multiple.
-
abs()
-
abs2() - Absolute value squared.
-
sign() - Returns -1, 0, or +1
-
signbit() - Similar to sign but technical.
-
copysign(x,y) - Magnitude x, with sign of y.
-
flipsign(x,y) - Magnitude x, with sign of x*y.
-
sqrt(x)
-
cbrt(x) - Cube root.
-
hypot(x,y) - Hypotenuse lenght of right triangle with x and y length sides.
-
exp(x) - e to the power.
-
expm1(x) - accurate
exp(x)-1
function for x near zero. -
log()
-
log2()
-
log10()
-
exponent(x) - binary exponent of x, whatever that means.
-
significand(x) - related to exponent().
All the trig functions seem present from sin()
to acsch()
and they
seem to operate in radians. There are a set of trig functions that
work in degrees too like sind()
.
All kinds of special functions are included out of the box like
besselj0(x)
and zeta(x)
and erf(x)
and on and on.
Rational Numbers
This interesting - fractions can be defined as fractions with //
. So
1//7
is one seventh. This means that isequal(1//3,.3333333333)
is
false. I don’t know what esoteric good this does since
isequal(1//3,1/3)
is true. But I’m not a mathematician.
Arrays
Arrays are quite well developed. They tend to be suited to technical tasks and multi-dimensional tricks. Extensive documentation exists
To initialize an Array:
bins= fill!(Array(Int,bincount),binsize)
Dicts
These are officially called associative collections.
They are created and used like this:
julia> pitch=Dict() # Probably better in this case:Dict{ASCIIString,Int32}()
Dict{Any,Any}()
julia> pitch["A"]=440
440
julia> pitch["C"]=523.3
523.3
julia> pitch
{"A"=>440,"C"=>523.3}
julia> states=["CA"=>"California","AK"=>"Alaska","OH"=>"Ohio"]
["CA"=>"California","OH"=>"Ohio","AK"=>"Alaska"]
julia> has(pitch,"C")
true
julia> delete!(pitch,"C")
523.3
Functions that work on Dicts include:
has (not haskey
as it says in the manual)
get (same as d[key]
syntax.
getkey - returns key or the default supplied.
delete! - Delete an item from a list.
pop! - Delete and return item or return default.
keys - Returns an iterator of keys.
collect(keys(d)) - Creates an array of keys.
values - Returns an iterator of values.
collect(values(d)) - Creates an array of values.
merge - Creates a new dict from the supplied dicts.
merge! - Updates first dict with the other ones.
sizehint(s,n) - Reserve capacity of n elements for collection s. An
optimization.
You can iterate over Dicts like this:
for dictitem in adictionary
println(dictitem[1]," ",dictitem[2]) # Produces "key value".
end
Strings
Strings are not exactly a real "type" but rather an "abstraction".
This means that many types can have a "string" representation. I guess
it’s like Python’s __str__()
.
There are "Char" types for single characters which can be specified with single quotes.
Unicode is painstakingly supported. But you you’re reading this, you can probably just pretend to always use English and not worry about the details.
Julia has Perl-compatible regular expressions (PCRE). Simple usage is
something like ismatch(r"^.o.k...","Monkeys")
producing true
.
There is a match()
function which returns the match. There is all
the rest of the normal and fancy functionality.
The whole
story.
Strings support all kinds of slice tricks. A cool one is
mystring[end]
and things like mystring[end-3]
or
mystring[end/4]
. There is the normal Python-like slice, but
something like abc[2:5]
(where abc
is the alphabet) returns
bcde
.
Commas concatenate strings into a list. Asterisks concatenate strings into a bigger string.
It’s possible to use $
to cause parts of strings to be interpreted
like Perl or Bash such as, "This is the alphabet: $abc"
. This looks
like it can make a mess. Of course to include a real dollar sign you
need to now do something special like "\$"
. See the macro example
(monetize) above.
Not to be outdone by Python’s str*n
.
julia> ^("-=",10)
"-=-=-=-=-=-=-=-=-=-="
Handy string functions:
-
endof()
-
length
-
start()
-
c,j= next(str,i)
-
ind2chr(str,i)
-
chr2ind(str,i)
-
split(str,[sep,[limit,][empty?]]) - Separators can be char, string, or re.
Functions
Functions can be defined like this:
function f(x,y)
x + y
end
Or:
f(x,y) = x + y
This is called with:
f(3,4)
Functions are objects and can be moved around with:
g = f
g(3,4)
Function arguments are passed "by sharing" though I fail to understand how that’s different from passing by reference. Whatever you call it, your objects can get modified when fed to a function.
There is a return
keyword which can proffer some specific result the
function should return, but it also suffices to just use the
function’s last statement’s result which will be the default returned
item.
Normal operators are functions and this can be delightful if you need to write some obfuscating code.
julia> *(+(3,7),+(5,5))
100
Or
julia> function exciting!(x); x*x; end
# method added to generic function exciting!
julia> exciting!(10)
100
Anonymous functions are supported like Python’s lambda
.
julia> r -> r^2+2r+3
# function
This can be used to feed the map
function.
Python Function Similarities
Tuples can be returned if multiple values need to be returned. Parentheses are optional. This is all just like Python.
There is a way to specify a variable number of arguments. Basically
somefunction(a,b,x...)
will need an argument for a
and one for b
and an iterable object for x
(a tuple, list, etc.).
Optional arguments can be preset like rotate(angle,radian=true)
. In
this case, radian
does not need to be specified unless needed.
There are keyword based arguments. This allows functions which could
have many possible arguments or options but only a few essential ones.
function cake(diameter, flavor; icingflavor="chocolate", sprinkles=false, text="Happy Birthday!")
There is even an interesting structure that reminds me of Python’s list comprehension.
julia> map( [1,2,3] ) do x ; x+3 ; end
3-element Int64 Array:
4
5
6
Macros
Macros are a bit like replacing it’s "call" with some code to be eval’ed.
julia> macro monetize(x)
"\$" * string(x)
end
julia> print(@monetize(33.25))
$33.25
Note that it’s easy to make a mess of things with macros (scope, clashing, etc). You have been warned.
Flow Control
Julia allows for sequence of expressions to be evaluated and the last one returned as part of another expression.
julia> h=(x=3;y=4;sqrt(x^2+y^2))
5.0
if - And Other Conditional Branching Tricks
The normal if
, elseif
, else
syntax is normal and ends with
end
:
if x > 5
println("Good job!")
elseif x > 0
println("Better than nothing.")
else
println("What's wrong with you?")
end
The if
statement’s conditional must be true
or false
(not 1
or
0
etc).
The C style condition ? do_if_true : do_if_false
syntax works.
The shell style ||
and &&
also work and only need to evaluate the
portions necessary to do the right thing.
x=5; x>3 && x<10 || "non"=="sense"
true
while
julia> i=3; while i>0 ; print(i) ; i-=1 ; end
321
for
julia> for i = 1:3 ; print(i) ; end
123
Here the 1:3
is a "Range object". Mercifully this Python idiom is
supported:
julia> l= ["ax","bow","club"]; for w = l ; println(w) ; end
ax
bow
club
There is a break
and continue
keyword. They do what Python
programmers expect.
IO and Files
Input can be done from basic IO streams with something like:
read(STDIN,x)
readbytes(STDIN,n)
readline(STDIN)
Functions like show
, print
, and write
can have an optional first
argument that is one of STDOUT
or STDERR
.
write(STDERR,"Problem!")
Read each line of a file with something like this.
open("input_file","r") do f
for line in EachLine(f)
println("read line: ", chomp(line))
end
end
Reading STDIN (a simple cat from standard input):
for line in EachLine(STDIN)
println("read line: ", chomp(line))
end
External Programs
Backticks produce command objects:
julia> run(`grep xed /etc/passwd`)
xed:x:1000:1000:Chris,,,:/home/xed:/bin/bash
julia> me=chomp(readall(`whoami`))
"xed"
julia> run(`grep processor /proc/cpuinfo` | `wc -l`)
2
The $
can be used to interpret variables in these objects.
Error Handling
Julia can throw
an exception. It can produce an error
oriented
exception. It can try
code and catch
exceptions. Regardless of how
the tried code works, a finally
clause can always run.
Interestingly, the manual says, "Don’t overuse try-catch. It is better to avoid errors than to rely on catching them." Sounds like something I’d say.
Consume and Produce
This arrangement assigns an on going task to a function and every time it’s called it makes progress in getting through the task. For example, if you need to go through a long input of numbers and for every number you need to do something to it, you could make a producer that gets each new number from the source. Then your consumer would request the next number and do something with it. This allows distinct functions to do the separate conceptual work, but interleave the execution.
TCP/IP
Interesting native client server functions. Documentation here.
Parallel Computing
Parallel computing features are highly developed. Full explanation here. Check out this cool ClusterManager functionality.
Calling C Code
Yes, it can be done. Details here.
Example
#!/usr/bin/julia
# Benchmark with a simple bin packing.o
const bincount= 5000::Int
const binsize= 100::Int
const maxitem= 70::Int
function next_item()
r::Int
r= floor(rand()*1000::Int) % maxitem + 1;
return r
end
bins= fill!(Array(Int,bincount),binsize)
still_room= true
while still_room
item= next_item()
for i = 1:bincount
if bins[i] >= item
bins[i] -= item
break
end
if i == bincount-1
still_room= false
end
end
end
println( sum(bins) )
Julia Official Manual TOC
-
http://docs.julialang.org/en/release-0.2/manual/introduction/
-
http://docs.julialang.org/en/release-0.2/manual/getting-started/
-
http://docs.julialang.org/en/release-0.2/manual/integers-and-floating-point-numbers/
-
http://docs.julialang.org/en/release-0.2/manual/mathematical-operations/
-
http://docs.julialang.org/en/release-0.2/manual/complex-and-rational-numbers/
-
http://docs.julialang.org/en/release-0.2/manual/control-flow/
-
http://docs.julialang.org/en/release-0.2/manual/variables-and-scoping/
-
http://docs.julialang.org/en/release-0.2/manual/constructors/
-
http://docs.julialang.org/en/release-0.2/manual/conversion-and-promotion/
-
http://docs.julialang.org/en/release-0.2/manual/metaprogramming/
-
http://docs.julialang.org/en/release-0.2/manual/linear-algebra/
-
http://docs.julialang.org/en/release-0.2/manual/networking-and-streams/
-
http://docs.julialang.org/en/release-0.2/manual/parallel-computing/
-
http://docs.julialang.org/en/release-0.2/manual/running-external-programs/
-
http://docs.julialang.org/en/release-0.2/manual/calling-c-and-fortran-code/
-
http://docs.julialang.org/en/release-0.2/manual/performance-tips/
-
http://docs.julialang.org/en/release-0.2/manual/style-guide/
-
http://docs.julialang.org/en/release-0.2/manual/noteworthy-differences/