= 1 # Assign an integer to x
x 2x # The result of 2 * x
2
Julia is a high-level, high-performance programming language primarily designed for numerical and scientific computing. Its syntax is familiar to users of other technical computing environments, while its flexibility and performance make it an excellent choice for a wide range of applications. In this section, we will look at a few simple examples to illustrate some core features of Julia and demonstrate its intuitive and powerful design.
In Julia, you can assign values to variables directly:
You can also perform mathematical operations directly on variables:
Julia allows you to use Unicode characters in your code, which makes it more expressive:
Julia even allows using emojis for variable names:
2.8284271247461903
Visit the list of Unicode Input for more examples.
In Julia, you can define a function using the function
keyword:
f (generic function with 1 method)
To evaluate a function, simply call it with an argument:
Julia also supports defintion of functions in assignement form, which are often used for short operations:
Julia also supports anonymous functions (functions without a name):
In some cases, you need to be cautious about operator precedence:
In Julia, functions can have side effects, meaning they modify variables or objects outside the scope of the function. Here’s an example:
Let’s consider the following vector:
You can access an element of the vector like this:
To update an element, simply reassign it:
If you mutate data inside a function, it will have side effects. For example, consider this function:
function f(x, y)
x[1] = 42 # Mutates x
y = 7 + sum(x) # New binding for y, no mutation
return y
end
a = [4, 5, 6]
b = 3
println("f($a, $b) = ", f(a, b)) # f modifies 'a' but not 'b'
println("a = ", a, " # a[1] is changed to 42 by f")
println("b = ", b, " # b remains unchanged")
f([4, 5, 6], 3) = 60
a = [42, 5, 6] # a[1] is changed to 42 by f
b = 3 # b remains unchanged
When a function has side effects, it’s a good practice to use the !
symbol at the end of the function’s name. This is called the bang convention, and it signals that the function mutates its arguments:
When you pass a slice of an array to a function in Julia, the slice is actually a copy, so modifying it does not alter the original array:
x = [1, 2, 3, 4]
println("x[2] before slice modification: ", x[2])
put_at_second_place!(x[1:3], 15) # Safe to modify the slice
println("x[2] after slice modification: ", x[2]) # Original array remains unchanged
x[2] before slice modification: 2
x[2] after slice modification: 2
When working with slices, remember that they are copies in Julia. Modifying a slice will not impact the original array, which helps prevent unintentional changes to your data.
Julia supports multiple methods for the same function name, which allows for more flexible and dynamic behavior. Here’s an example:
You can define several methods for the same function with different types:
Calling the function:
If you call Σ
with arguments that don’t match the types, Julia will throw an error:
MethodError: no method matching Σ(::Int64, ::Float64) The function `Σ` exists, but no method is defined for this combination of argument types. Closest candidates are: Σ(::Float64, ::Float64) @ Main In[18]:1 Stacktrace: [1] top-level scope @ In[20]:1
You can define more methods that work with different types:
Julia will select the appropriate method based on the argument types:
In Julia, iterators allow you to loop through collections in a memory-efficient way. Here’s an example of using 1:5
as an iterator:
This prints the numbers from 1 to 5. You can also iterate through ranges and collections:
Julia’s Iterators
package allows for lazy collections, where values are computed on demand. Here’s an example:
using Base.Iterators: cycle
round = 1
for i in cycle([1, 2, 3])
println(i)
if i == 3
if round == 2
break
else
round += 1
end
end
end
1
2
3
1
2
3
This loops over the values 1, 2, and 3, repeating as a cycle.
Julia has type stability for fast compilation and execution. When writing functions, it’s important to ensure that the type of the return value can be determined without ambiguity.
Example of type instability:
function f(x)
if x > 0
return 1
else
return 0.0
end
end
println("The value 2 of type ", typeof( 2), " produces an output of type ", typeof(f( 2)))
println("The value -2 of type ", typeof(-2), " produces an output of type ", typeof(f(-2)))
The value 2 of type Int64 produces an output of type Int64
The value -2 of type Int64 produces an output of type Float64
Julia is dynamically typed, but ensuring type stability within functions helps the compiler optimize code for better performance.
For better performance, always try to ensure type stability in your functions. This can be achieved by making the return type predictable, from the types of input variables and not their values.