{ "cells": [ { "cell_type": "markdown", "source": [ "# Intro to Julia\n", "**Originally Contributed by**: Juan Pablo Vielma" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "## Introduction" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Since JuMP is embedded in Julia, knowing some basic Julia is important\n", "for learning JuMP. This notebook is designed to provide a minimalist\n", "crash course in the basics of Julia. You can find resources that provide\n", "a more comprehensive introduction to Julia [here](https://julialang.org/learning/)." ], "metadata": {} }, { "cell_type": "markdown", "source": [ "### How to Print\n", "In Julia, we usually use println() to print" ], "metadata": {} }, { "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hello, World!\n" ] } ], "cell_type": "code", "source": [ "println(\"Hello, World!\")" ], "metadata": {}, "execution_count": 1 }, { "cell_type": "markdown", "source": [ "### Basic Data Types\n", "Integers" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "Int64" }, "metadata": {}, "execution_count": 2 } ], "cell_type": "code", "source": [ "typeof(1 + -2)" ], "metadata": {}, "execution_count": 2 }, { "cell_type": "markdown", "source": [ "Floating point numbers" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "Float64" }, "metadata": {}, "execution_count": 3 } ], "cell_type": "code", "source": [ "typeof(1.2 - 2.3)" ], "metadata": {}, "execution_count": 3 }, { "cell_type": "markdown", "source": [ "There are also some cool things like an irrational representation of π. To make π\n", "(and most other greek letters), type \\pi and then press [TAB]." ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "Irrational{:π}" }, "metadata": {}, "execution_count": 4 } ], "cell_type": "code", "source": [ "π\n", "typeof(π)" ], "metadata": {}, "execution_count": 4 }, { "cell_type": "markdown", "source": [ "Julia has native support for complex numbers" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "Complex{Int64}" }, "metadata": {}, "execution_count": 5 } ], "cell_type": "code", "source": [ "typeof(2 + 3im)" ], "metadata": {}, "execution_count": 5 }, { "cell_type": "markdown", "source": [ "Double quotes are used for strings" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "String" }, "metadata": {}, "execution_count": 6 } ], "cell_type": "code", "source": [ "typeof(\"This is Julia\")" ], "metadata": {}, "execution_count": 6 }, { "cell_type": "markdown", "source": [ "Unicode is fine in strings" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "Symbol" }, "metadata": {}, "execution_count": 7 } ], "cell_type": "code", "source": [ "typeof(\"π is about 3.1415\")\n", "\n", "#'Julia symbols provide a way to make human readable unique identifiers.\n", "\n", ":my_id\n", "typeof(:my_id)" ], "metadata": {}, "execution_count": 7 }, { "cell_type": "markdown", "source": [ "### Arithmetic and Equality Testing" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Julia is great for math" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "4 - 3im" }, "metadata": {}, "execution_count": 8 } ], "cell_type": "code", "source": [ "1 + 1\n", "\n", "#'Even math involving complex numbers\n", "\n", "(2 + 1im) * (1 - 2im)" ], "metadata": {}, "execution_count": 8 }, { "cell_type": "markdown", "source": [ "We can also write things like the following using √ (\\sqrt)" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "false" }, "metadata": {}, "execution_count": 9 } ], "cell_type": "code", "source": [ "sin(2π / 3) == √3 / 2" ], "metadata": {}, "execution_count": 9 }, { "cell_type": "markdown", "source": [ "Wait. What???" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "1.1102230246251565e-16" }, "metadata": {}, "execution_count": 10 } ], "cell_type": "code", "source": [ "sin(2π / 3) - √3 / 2" ], "metadata": {}, "execution_count": 10 }, { "cell_type": "markdown", "source": [ "Let's try again using ≈ (\\approx)." ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "true" }, "metadata": {}, "execution_count": 11 } ], "cell_type": "code", "source": [ "sin(2π / 3) ≈ √3 / 2" ], "metadata": {}, "execution_count": 11 }, { "cell_type": "markdown", "source": [ "Note that this time we used ≈ instead of ==. That is because computers don't use\n", "real numbers. They use a discrete representation called floating point. If you aren't\n", "careful, this can throw up all manner of issues. For example:" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "true" }, "metadata": {}, "execution_count": 12 } ], "cell_type": "code", "source": [ "1 + 1e-16 == 1" ], "metadata": {}, "execution_count": 12 }, { "cell_type": "markdown", "source": [ "It even turns out that floating point numbers aren't associative!" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "false" }, "metadata": {}, "execution_count": 13 } ], "cell_type": "code", "source": [ "(1 + 1e-16) - 1e-16 == 1 + (1e-16 - 1e-16)" ], "metadata": {}, "execution_count": 13 }, { "cell_type": "markdown", "source": [ "### Vectors, Matrices and Arrays\n", "Similar to Matlab, Julia has native support for vectors, matrices and tensors; all of which are represented by arrays of different dimensions.\n", "Vectors are constructed by comma-separated elements surrounded by square brackets:" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "2-element Vector{Int64}:\n 5\n 6" }, "metadata": {}, "execution_count": 14 } ], "cell_type": "code", "source": [ "b = [5, 6]" ], "metadata": {}, "execution_count": 14 }, { "cell_type": "markdown", "source": [ "Matrices can by constructed with spaces separating the columns, and semicolons separating the rows:" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "2×2 Matrix{Int64}:\n 1 2\n 3 4" }, "metadata": {}, "execution_count": 15 } ], "cell_type": "code", "source": [ "A = [1 2; 3 4]" ], "metadata": {}, "execution_count": 15 }, { "cell_type": "markdown", "source": [ "We can do linear algebra:" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "2-element Vector{Float64}:\n -3.9999999999999987\n 4.499999999999999" }, "metadata": {}, "execution_count": 16 } ], "cell_type": "code", "source": [ "x = A \\ b" ], "metadata": {}, "execution_count": 16 }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "2-element Vector{Float64}:\n 5.0\n 6.0" }, "metadata": {}, "execution_count": 17 } ], "cell_type": "code", "source": [ "A * x" ], "metadata": {}, "execution_count": 17 }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "true" }, "metadata": {}, "execution_count": 18 } ], "cell_type": "code", "source": [ "A * x == b" ], "metadata": {}, "execution_count": 18 }, { "cell_type": "markdown", "source": [ "Note that when multiplying vectors and matrices, dimensions matter. For example, you can't multiply a vector by a vector:\n", "```julia\n", "b * b\n", "\n", "MethodError: no method matching *(::Array{Int64,1}, ::Array{Int64,1})\n", "Closest candidates are:\n", " *(::Any, ::Any, !Matched::Any, !Matched::Any...) at operators.jl:529\n", " *(!Matched::LinearAlgebra.Adjoint{#s617,#s616} where #s616<:Union{DenseArray{T<:Union{Complex{Float32}, Complex{Float64}, Float32, Float64},2}, Base.ReinterpretArray{T<:Union{Complex{Float32}, Complex{Float64}, Float32, Float64},2,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray}, Base.ReshapedArray{T<:Union{Complex{Float32}, Complex{Float64}, Float32, Float64},2,A,MI} where MI<:Tuple{Vararg{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64},N} where N} where A<:Union{Base.ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray}, SubArray{T<:Union{Complex{Float32}, Complex{Float64}, Float32, Float64},2,A,I,L} where L where I<:Tuple{Vararg{Union{Int64, AbstractRange{Int64}, Base.AbstractCartesianIndex},N} where N} where A<:Union{Base.ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, Base.ReshapedArray{T,N,A,MI} where MI<:Tuple{Vararg{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64},N} where N} where A<:Union{Base.ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, DenseArray}} where #s617, ::Union{DenseArray{S,1}, Base.ReinterpretArray{S,1,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray}, Base.ReshapedArray{S,1,A,MI} where MI<:Tuple{Vararg{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64},N} where N} where A<:Union{Base.ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray}, SubArray{S,1,A,I,L} where L where I<:Tuple{Vararg{Union{Int64, AbstractRange{Int64}, Base.AbstractCartesianIndex},N} where N} where A<:Union{Base.ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, Base.ReshapedArray{T,N,A,MI} where MI<:Tuple{Vararg{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64},N} where N} where A<:Union{Base.ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, DenseArray}}) where {T<:Union{Complex{Float32}, Complex{Float64}, Float32, Float64}, S} at /Users/sabae/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.2/LinearAlgebra/src/matmul.jl:99\n", " *(!Matched::LinearAlgebra.Adjoint{#s617,#s616} where #s616<:LinearAlgebra.AbstractTriangular where #s617, ::AbstractArray{T,1} where T) at /Users/sabae/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.2/LinearAlgebra/src/triangular.jl:1850\n", " ...\n", "\n", "Stacktrace:\n", " [1] top-level scope at In[16]:4\n", "```" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "But multiplying transposes works:" ], "metadata": {} }, { "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "b' * b = 61\n", "b * b' = [25 30; 30 36]\n" ] } ], "cell_type": "code", "source": [ "@show b' * b\n", "@show b * b';" ], "metadata": {}, "execution_count": 19 }, { "cell_type": "markdown", "source": [ "### Tuples\n", "Julia makes extensive use of a simple data structure called Tuples. Tuples are immutable collections of values.\n", "For example," ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "Tuple{String, Float64, Symbol}" }, "metadata": {}, "execution_count": 20 } ], "cell_type": "code", "source": [ "t = (\"hello\", 1.2, :foo)\n", "typeof(t)" ], "metadata": {}, "execution_count": 20 }, { "cell_type": "markdown", "source": [ "Tuples can be accessed by index, similar to arrays," ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "1.2" }, "metadata": {}, "execution_count": 21 } ], "cell_type": "code", "source": [ "t[2]" ], "metadata": {}, "execution_count": 21 }, { "cell_type": "markdown", "source": [ "And can be \"unpacked\" like so," ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "1.2" }, "metadata": {}, "execution_count": 22 } ], "cell_type": "code", "source": [ "a, b, c = t\n", "b" ], "metadata": {}, "execution_count": 22 }, { "cell_type": "markdown", "source": [ "The values can also be given names, which is a convenient way of making light-weight data structures." ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "(word = \"hello\", num = 1.2, sym = :foo)" }, "metadata": {}, "execution_count": 23 } ], "cell_type": "code", "source": [ "t = (word = \"hello\", num = 1.2, sym = :foo)" ], "metadata": {}, "execution_count": 23 }, { "cell_type": "markdown", "source": [ "Then values can be accessed using a dot syntax," ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "\"hello\"" }, "metadata": {}, "execution_count": 24 } ], "cell_type": "code", "source": [ "t.word" ], "metadata": {}, "execution_count": 24 }, { "cell_type": "markdown", "source": [ "### Dictionaries\n", "Similar to Python, Julia has native support for dictionaries. Dictionaries provide a very generic way of mapping keys to values. For example, a map of integers to strings," ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "Dict{Int64, String} with 3 entries:\n 4 => \"D\"\n 2 => \"B\"\n 1 => \"A\"" }, "metadata": {}, "execution_count": 25 } ], "cell_type": "code", "source": [ "d1 = Dict(1 => \"A\", 2 => \"B\", 4 => \"D\")" ], "metadata": {}, "execution_count": 25 }, { "cell_type": "markdown", "source": [ "Looking up a values uses the bracket syntax," ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "\"B\"" }, "metadata": {}, "execution_count": 26 } ], "cell_type": "code", "source": [ "d1[2]" ], "metadata": {}, "execution_count": 26 }, { "cell_type": "markdown", "source": [ "Dictionaries support non-integer keys and can mix data types," ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "Dict{String, Number} with 3 entries:\n \"B\" => 2.5\n \"A\" => 1\n \"D\" => 2-3im" }, "metadata": {}, "execution_count": 27 } ], "cell_type": "code", "source": [ "Dict(\"A\" => 1, \"B\" => 2.5, \"D\" => 2 - 3im)" ], "metadata": {}, "execution_count": 27 }, { "cell_type": "markdown", "source": [ "Dictionaries can be nested" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "3" }, "metadata": {}, "execution_count": 28 } ], "cell_type": "code", "source": [ "d2 = Dict(\"A\" => 1, \"B\" => 2, \"D\" => Dict(:foo => 3, :bar => 4))\n", "d2[\"B\"]\n", "d2[\"D\"][:foo]" ], "metadata": {}, "execution_count": 28 }, { "cell_type": "markdown", "source": [ "### For-Each Loops\n", "Julia has native support for for-each style loops with the syntax `for in end`." ], "metadata": {} }, { "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1\n", "2\n", "3\n", "4\n", "5\n" ] } ], "cell_type": "code", "source": [ "for i in 1:5\n", " println(i)\n", "end" ], "metadata": {}, "execution_count": 29 }, { "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1.2\n", "2.3\n", "3.4\n", "4.5\n", "5.6\n" ] } ], "cell_type": "code", "source": [ "for i in [1.2, 2.3, 3.4, 4.5, 5.6]\n", " println(i)\n", "end" ], "metadata": {}, "execution_count": 30 }, { "cell_type": "markdown", "source": [ "This for-each loop also works with dictionaries." ], "metadata": {} }, { "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "B: 2.5\n", "A: 1\n", "D: 2 - 3im\n" ] } ], "cell_type": "code", "source": [ "for (key, value) in Dict(\"A\" => 1, \"B\" => 2.5, \"D\" => 2 - 3im)\n", " println(\"$key: $value\")\n", "end" ], "metadata": {}, "execution_count": 31 }, { "cell_type": "markdown", "source": [ "Note that in contrast to vector languages like Matlab and R, loops do not result in a significant performance degradation in Julia." ], "metadata": {} }, { "cell_type": "markdown", "source": [ "### Control Flow\n", "Julia control flow is similar to Matlab, using the keywords `if-elseif-else-end`, and the logical operators `||` and `&&` for *or* and *and* respectively." ], "metadata": {} }, { "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 is less than 5\n", "3 is less than 5\n", "6 is less than 10\n", "9 is less than 10\n", "12 is bigger than 10\n", "15 is bigger than 10\n" ] } ], "cell_type": "code", "source": [ "i = 10\n", "for i in 0:3:15\n", " if i < 5\n", " println(\"$(i) is less than 5\")\n", " elseif i < 10\n", " println(\"$(i) is less than 10\")\n", " else\n", " if i == 10\n", " println(\"the value is 10\")\n", " else\n", " println(\"$(i) is bigger than 10\")\n", " end\n", " end\n", "end" ], "metadata": {}, "execution_count": 32 }, { "cell_type": "markdown", "source": [ "### Comprehensions\n", "Similar to languages like Haskell and Python, Julia supports the use of simple loops in the construction of arrays and dictionaries, called comprehenions.\n", "\n", "A list of increasing integers," ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "5-element Vector{Int64}:\n 1\n 2\n 3\n 4\n 5" }, "metadata": {}, "execution_count": 33 } ], "cell_type": "code", "source": [ "[i for i in 1:5]" ], "metadata": {}, "execution_count": 33 }, { "cell_type": "markdown", "source": [ "Matrices can be built by including multiple indices," ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "5×6 Matrix{Int64}:\n 5 6 7 8 9 10\n 10 12 14 16 18 20\n 15 18 21 24 27 30\n 20 24 28 32 36 40\n 25 30 35 40 45 50" }, "metadata": {}, "execution_count": 34 } ], "cell_type": "code", "source": [ "[i * j for i in 1:5, j in 5:10]" ], "metadata": {}, "execution_count": 34 }, { "cell_type": "markdown", "source": [ "Conditional statements can be used to filter out some values," ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "5-element Vector{Int64}:\n 1\n 3\n 5\n 7\n 9" }, "metadata": {}, "execution_count": 35 } ], "cell_type": "code", "source": [ "[i for i in 1:10 if i % 2 == 1]" ], "metadata": {}, "execution_count": 35 }, { "cell_type": "markdown", "source": [ "A similar syntax can be used for building dictionaries" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "Dict{String, Int64} with 5 entries:\n \"1\" => 1\n \"5\" => 5\n \"7\" => 7\n \"9\" => 9\n \"3\" => 3" }, "metadata": {}, "execution_count": 36 } ], "cell_type": "code", "source": [ "Dict(\"$i\" => i for i in 1:10 if i % 2 == 1)" ], "metadata": {}, "execution_count": 36 }, { "cell_type": "markdown", "source": [ "### Functions\n", "A simple function is defined as follows," ], "metadata": {} }, { "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "hello\n" ] } ], "cell_type": "code", "source": [ "function print_hello()\n", " println(\"hello\")\n", "end\n", "print_hello()" ], "metadata": {}, "execution_count": 37 }, { "cell_type": "markdown", "source": [ "Arguments can be added to a function," ], "metadata": {} }, { "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "hello\n", "1.234\n", "my_id\n" ] } ], "cell_type": "code", "source": [ "function print_it(x)\n", " println(x)\n", "end\n", "print_it(\"hello\")\n", "print_it(1.234)\n", "print_it(:my_id)" ], "metadata": {}, "execution_count": 38 }, { "cell_type": "markdown", "source": [ "Optional keyword arguments are also possible" ], "metadata": {} }, { "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "value: 1.234\n", "val: 1.234\n" ] } ], "cell_type": "code", "source": [ "function print_it(x; prefix = \"value:\")\n", " println(\"$(prefix) $x\")\n", "end\n", "print_it(1.234)\n", "print_it(1.234, prefix = \"val:\")" ], "metadata": {}, "execution_count": 39 }, { "cell_type": "markdown", "source": [ "The keyword `return` is used to specify the return values of a function." ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "20.0" }, "metadata": {}, "execution_count": 40 } ], "cell_type": "code", "source": [ "function mult(x; y = 2.0)\n", " return x * y\n", "end\n", "mult(4.0)\n", "mult(4.0, y = 5.0)" ], "metadata": {}, "execution_count": 40 }, { "cell_type": "markdown", "source": [ "### Other notes on types\n", "Usually, specifing types is not required to use Julia. However, it can be helpful to understand the basics of Julia types for debugging.\n", "For example this list has a type of `Array{Int64,1}` indicating that it is a one dimensional array of integer values." ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "4-element Vector{Int64}:\n 1\n 5\n -2\n 7" }, "metadata": {}, "execution_count": 41 } ], "cell_type": "code", "source": [ "[1, 5, -2, 7]" ], "metadata": {}, "execution_count": 41 }, { "cell_type": "markdown", "source": [ "In this example, the decimal values lead to a one dimensional array of floating point values, i.e. `Array{Float64,1}`. Notice that the integer `7` is promoted to a `Float64`, because all elements in the array need share a common type." ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "4-element Vector{Float64}:\n 1.0\n 5.2\n -2.1\n 7.0" }, "metadata": {}, "execution_count": 42 } ], "cell_type": "code", "source": [ "[1.0, 5.2, -2.1, 7]" ], "metadata": {}, "execution_count": 42 }, { "cell_type": "markdown", "source": [ "### Mutable vs immutable objects\n", "Some types in Julia are *mutable*, which means you can change the values inside them. A good example is an array. You can modify the contents of an array without having to make a new array.\n", "In contrast, types like `Float64` are *immutable*. You can't modify the contents of a `Float64`.\n", "This is something to be aware of when passing types into functions. For example:" ], "metadata": {} }, { "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "mutable_type: [2, 2, 3]\n", "immutable_type: 1\n" ] } ], "cell_type": "code", "source": [ "function mutability_example(mutable_type::Vector{Int}, immutable_type::Int)\n", " mutable_type[1] += 1\n", " immutable_type += 1\n", " return\n", "end\n", "\n", "mutable_type = [1, 2, 3]\n", "immutable_type = 1\n", "\n", "mutability_example(mutable_type, immutable_type)\n", "\n", "println(\"mutable_type: $(mutable_type)\")\n", "println(\"immutable_type: $(immutable_type)\")" ], "metadata": {}, "execution_count": 43 }, { "cell_type": "markdown", "source": [ "Because `Vector{Int}` is a mutable type, modifying the variable inside the function changed the value outside of the function. In constrast, the change to `immutable_type` didn't modify the value outside the function.\n", "You can check mutability with the `isimmutable` function." ], "metadata": {} }, { "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "isimmutable([1, 2, 3]) = false\n", "isimmutable(1) = true\n" ] } ], "cell_type": "code", "source": [ "@show isimmutable([1, 2, 3])\n", "@show isimmutable(1);" ], "metadata": {}, "execution_count": 44 }, { "cell_type": "markdown", "source": [ "### Using Packages and the Package Manager\n", "No matter how wonderful Julia's base language is, at some point you will want to use an extension package. Some of these are built-in, for example random number generation is available in the `Random` package in the standard library. These packages are loaded with the commands `using` and `import`." ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "10-element Vector{Float64}:\n 0.2527022028067233\n 0.08153972296802703\n 0.7301482223395156\n 0.7978742531389277\n 0.8256166938467838\n 0.875925090520644\n 0.9227006218005859\n 0.5826002027795376\n 0.838343807337677\n 0.6003642780683117" }, "metadata": {}, "execution_count": 45 } ], "cell_type": "code", "source": [ "using Random\n", "[rand() for i in 1:10]" ], "metadata": {}, "execution_count": 45 }, { "cell_type": "markdown", "source": [ "The Package Manager is used to install packages that are not part of Julia's standard library.\n", "For example the following can be used to install JuMP," ], "metadata": {} }, { "cell_type": "markdown", "source": [ "```julia\n", "using Pkg\n", "Pkg.add(\"JuMP\")\n", "```" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "For a complete list of registed Julia packages see the package listing at https://pkg.julialang.org/.\n", "From time to you may wish to use a Julia package that is not registered. In this case a git repository URL can be used to install the package." ], "metadata": {} }, { "cell_type": "markdown", "source": [ "```julia\n", "using Pkg\n", "Pkg.add(\"https://github.com/user-name/MyPackage.jl.git\")\n", "```" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Note that for clarity this example uses the package manager `Pkg`. Julia includes an interactive package manager that can be accessed using `]`. [This video](https://youtu.be/76KL8aSz0Sg) gives an overview of using the interactive package manager environment.\n", "The state of installed packages can also be saved in two files: `Project.toml` and `Manifest.toml`. If these files are stored in the same directory than a notebook, the state of the packages can be recovered by running\n", "```julia\n", "import Pkg\n", "Pkg.activate(@__DIR__)\n", "Pkg.instantiate()\n", "```" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "### HELP!\n", "Julia includes a help mode that can be accessed using `?`. Entering any object (e.g. function, type, struct, ...) into the help mode will show its documentation, if any is available." ], "metadata": {} }, { "cell_type": "markdown", "source": [ "### Some Common Gotchas" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "#### MethodError\n", "A common error in Julia is `MethodError`, which indicates that the function is not defined for the given value. For example, by default the `ceil` function is not defined for complex numbers. The \"closest candidates\" list suggest some Julia types that the function is defined for." ], "metadata": {} }, { "cell_type": "markdown", "source": [ "```julia\n", "ceil(1.2 + 2.3im)\n", "```" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "---\n", "\n", "*This notebook was generated using [Literate.jl](https://github.com/fredrikekre/Literate.jl).*" ], "metadata": {} } ], "nbformat_minor": 3, "metadata": { "language_info": { "file_extension": ".jl", "mimetype": "application/julia", "name": "julia", "version": "1.7.2" }, "kernelspec": { "name": "julia-1.7", "display_name": "Julia 1.7.2", "language": "julia" } }, "nbformat": 4 }