Julia: Tutorial & Code-Collection¶

Source: https://github.com/markomlikota/CodingSoftware

MIT License, © Marko Mlikota, https://markomlikota.github.io

Functions¶

In [1]:
# Above, we used functions that are pre-programmed in Julia
# or contained in a package we load. 
# We can of course also define our own functions:

f1(x) = 2 * x
# or, in long form:
function f2(x)
    return 2 * x
end

println("f1(3) = ",f1(3),", f2(3) = ",f2(3))
f1(3) = 6, f2(3) = 6
In [2]:
# Can also define function without arguments:

fMyFun() = 5

fMyFun()
Out[2]:
5
In [3]:
# Multiple arguments:

function fMyFun(x,y,z=3)
# z has default value 3
   helper = 2*(x+5)
    return helper - y + z
end
        
fMyFun(6,4,2)
fMyFun(6,4,3)
fMyFun(6,4)
Out[3]:
21
In [4]:
# One can specify the type of argument that a function takes.
fMyFun(x::Int) = 4*x
fMyFun(3) # works
fMyFun(3.0) # gives error
MethodError: no method matching fMyFun(::Float64)

Closest candidates are:
  fMyFun(::Any, ::Any, ::Any)
   @ Main In[3]:3
  fMyFun(::Any, ::Any)
   @ Main In[3]:3
  fMyFun(::Int64)
   @ Main In[4]:2
  ...


Stacktrace:
 [1] top-level scope
   @ In[4]:4
In [5]:
# This way, one can also let a function do different things depending on the type of input:
# (though this can also be done using a conditional; see below)

fMyFun(x::Int) = 4*x
fMyFun(x::Float64) = 2*x

fMyFun(3)
fMyFun(3.0)

# Of course, just like it can take on multiple arguments,
# a function can return multiple outputs:

function fABC(x)
    output1 = x*2
    output2 = x*5
    return output1, output2
end

fABC(5)
Out[5]:
(10, 25)
In [6]:
# Extract both outputs:
o1, o2 = fABC(5)

# Extracting only the needed outputs can have efficiency gains.

# To extract just the first output:
a1, _ = fABC(5)

# To extract just the second output:
_, a2 = fABC(5)
Out[6]:
(10, 25)
In [7]:
# A function can map all sorts of objects into all sorts of objects.
# e.g. it can take on a function as an argument:

function fUseFunction(fOriginalFun,x)
    return fOriginalFun(5)*x
end
fOriginalFun(x) = x/3
fUseFunction(fOriginalFun,9)
Out[7]:
15.0
In [8]:
# A function can also return a function:

function fGetFun(x)
    function fMyInsideFunction(y)
        return x*y
    end
    return fMyInsideFunction
end

fMyInsideFunction = fGetFun(5)

fMyInsideFunction(3)

# Later, we'll discuss functions that use vectors or matrices as inputs and/or outputs.
Out[8]:
15
In [9]:
# We can create a new function by combining several other functions: 

f1(x) = x^2
f2(x) = x^3

f(x) = f1(x) + f2(x)
f(4)

# Of course, changing f1 or f2 will also change f, then:
f1(x) = 1.5 * x^2
f(4)
Out[9]:
88.0
In [10]:
# It is good practice to have a granular code, 
# where larger tasks are divided into many smaller tasks,
# each of which is performed by a function.

# When calling functions inside functions, 
# keep in mind variable scopes (see below).
In [11]:
# A note on redefining functions in Julia.
# You might get unexpected errors when redefining functions.

# The above two functions are so-caled "anonymous" functions.
# They can easily be modified:
println("f1(3) = ",f1(3),", f2(3) = ",f2(3))
f1(x) = 5 * x
function f2(x)
    return 5 * x
end
#
println("f1(3) = ",f1(3),", f2(3) = ",f2(3))
f1(3) = 13.5, f2(3) = 27
f1(3) = 15, f2(3) = 15
In [12]:
# There are two ways to define "non-anonymous" functions in Julia:
f3 = x -> 2 * x
f4 = function(x)
    return 2 * x
end
Out[12]:
#4 (generic function with 1 method)
In [13]:
# These can only be modified by re-defining them as again
# "non-anonymous" functions:
#
println("f3(3) = ",f3(3),", f4(3) = ",f4(3))
#
f3 = x-> 5 * x
f4 = function(x)
    return 5 * x
end
#
println("f3(3) = ",f3(3),", f4(3) = ",f4(3))
f3(3) = 6, f4(3) = 6
f3(3) = 15, f4(3) = 15
In [14]:
# 
# but not as anonymous functions:
f3(x) = 7 * x
function f4(x)
    return 7 * x
end
cannot define function f3; it already has a value

Stacktrace:
 [1] top-level scope
   @ none:0
 [2] top-level scope
   @ In[14]:3

Try Exercises 3.a.i - 3.a.iii