Документация Engee
Notebook

Julia: Возможности языка

Данная статья представляет собой сжатый справочник по основам языка Julia, построенный по принципу «минимум теории — максимум практики». Весь материал организован вокруг кода: каждый раздел иллюстрируется непосредственно работающими примерами, а текстовые пояснения сведены к необходимому минимуму. Такой подход позволяет быстро погрузиться в синтаксис, увидеть типичные конструкции и сразу начать экспериментировать, не отвлекаясь на длинные описания. Julia славится своей выразительностью и скоростью, и лучше всего знакомиться с ней через живой код — именно эту возможность даёт предлагаемая подборка. Здесь вы найдёте примеры работы с переменными, типами данных, коллекциями, функциями, множественной диспетчеризацией и другими ключевыми возможностями языка.

Переменные и присваивание

Присвоение целого числа

In [ ]:
x = 100              
Out[0]:
100

Использование переменной в выражении

In [ ]:
x * 5              
Out[0]:
500

Переназначение переменной

In [ ]:
x = 10 + 10    
Out[0]:
20

Julia - динамически типизированный язык, переменная может менять тип

In [ ]:
x = "Hello World!"   
Out[0]:
"Hello World!"

Присваивание возвращает значение правой части

In [ ]:
b=4
a = (b = 2 + 2) * 5
Out[0]:
20

Возможны так же цепочки присваиваний, а примере ниже всем трём переменным присвоено 10

In [ ]:
x = y = z = 10
Out[0]:
10

Поддерживается Unicode в именах переменных

In [ ]:
х1 = 100             
 = "Точка"         
δ = 0.001            
Out[0]:
0.001

Регистрозависимость, myvar и MyVar - это две разные переменные.

In [ ]:
myvar = 1
Out[0]:
1
In [ ]:
MyVar = 2
Out[0]:
2

Запрещённые имена для переменных:

  • Нельзя начинать с цифры: 1x = 100
  • Нельзя использовать функции языка как переменные: baremodule, begin, break, catch, const, continue, do, else, elseif, end, export, false, finally, for, function, global, if, import, let, local, macro, module, quote, return, struct, true, try, using, while

Поведение с мутабельными типами (массивы)
Важно: присваивание НЕ создаёт копию для мутабельных объектов!

In [ ]:
a = [1, 2, 3] 
Out[0]:
3-element Vector{Int64}:
 1
 2
 3

b ссылается на ТОТ ЖЕ массив, что и a

In [ ]:
b = a 
Out[0]:
3-element Vector{Int64}:
 1
 2
 3

Мутируем массив через a и выводим b, b тоже видит изменение!

In [ ]:
a[1] = 42
b
Out[0]:
3-element Vector{Int64}:
 42
  2
  3

Перепривязка массива

In [ ]:
a = [1, 2, 3]
b = a
a = 3.14159
b
Out[0]:
3-element Vector{Int64}:
 1
 2
 3
In [ ]:
a = [1, 2, 3]
b .= a
a = [1, 2, 30] 
b
Out[0]:
3-element Vector{Int64}:
 1
 2
 3

Типы данных и аннотации

Автоматическое определение типа, Float64 - тип по умолчанию для чисел с плавающей точкой

In [ ]:
x = -2.0
typeof(x)
Out[0]:
Float64

Int64 - тип по умолчанию для целых чисел

In [ ]:
x = 2
typeof(x)
Out[0]:
Int64
In [ ]:
x = -2
typeof(x)
Out[0]:
Int64

Ошибка домена без комплексных чисел

In [ ]:
# sqrt(x) #ERROR

Явное указание комплексного типа. Добавляем 0im для комплексного типа

In [ ]:
x = -2.0 + 0im        
typeof(x) 
Out[0]:
ComplexF64 (alias for Complex{Float64})
In [ ]:
sqrt(x)              
Out[0]:
0.0 + 1.4142135623730951im

Оператор аннотации типа :: , проверка утверждение "является экземпляром" заданного типа или нет.

In [ ]:
(2 + 2)::Int 
Out[0]:
4
In [ ]:
# (2 + 2)::AbstractFloat #ERROR
In [ ]:
(2.0 + 2.0)::AbstractFloat
Out[0]:
4.0

Проверка стабильности типов

In [ ]:
function fast_sum(arr::Vector{Int})
    s = 0
    for x in arr
        s += x
    end
    return s
end
@code_warntype fast_sum([1,2,3])   # должен показать Any::Any (или конкретные типы)
# @code_native fast_sum([1,2,3])   # ассемблерный код
MethodInstance for fast_sum(::Vector{Int64})
  from fast_sum(arr::Vector{Int64}) @ Main In[57]:1
Arguments
  #self#::Core.Const(Main.fast_sum)
  arr::Vector{Int64}
Locals
  @_3::Union{Nothing, Tuple{Int64, Int64}}
  s::Int64
  x::Int64
Body::Int64
1 ─       (s = 0)
 %2  = arr::Vector{Int64}
       (@_3 = Base.iterate(%2))
 %4  = @_3::Union{Nothing, Tuple{Int64, Int64}}
 %5  = (%4 === nothing)::Bool
 %6  = Base.not_int(%5)::Bool
└──       goto #4 if not %6
2 ┄ %8  = @_3::Tuple{Int64, Int64}
       (x = Core.getfield(%8, 1))
 %10 = Core.getfield(%8, 2)::Int64
 %11 = Main.:+::Core.Const(+)
 %12 = s::Int64
 %13 = x::Int64
       (s = (%11)(%12, %13))
       (@_3 = Base.iterate(%2, %10))
 %16 = @_3::Union{Nothing, Tuple{Int64, Int64}}
 %17 = (%16 === nothing)::Bool
 %18 = Base.not_int(%17)::Bool
└──       goto #4 if not %18
3 ─       goto #2
4 ┄ %21 = s::Int64
└──       return %21

Объявление типа переменной

In [ ]:
x = Complex(-2)   
typeof(x)                  
Out[0]:
Complex{Int64}
In [ ]:
sqrt(x)
Out[0]:
0.0 + 1.4142135623730951im

Иерархия типов:

  • Int64 <: Integer <: Real <: Number <: Any
  • Float64 <: AbstractFloat <: Real <: Number <: Any

Параметрические типы

Двумерный массив Int64

In [ ]:
Array{Int64, 2}
Out[0]:
Matrix{Int64} (alias for Array{Int64, 2})

Вектор Float64 (одномерный)

In [ ]:
Array{Float64, 1}
Out[0]:
Vector{Float64} (alias for Array{Float64, 1})

Целые числа

Псевдонимы системных типов:

Int - Int64 целое

UInt - UInt64 беззнаковое целое

In [ ]:
10 
Out[0]:
10

Ведущий ноль не делает число восьмеричным

In [ ]:
0123456789
Out[0]:
123456789

Поддерживается автоматическое расширение битности для больших чисел

In [ ]:
typeof(10000000000000000000)
Out[0]:
Int128
In [ ]:
typeof(1000000000000000000000000000000000000000)
Out[0]:
BigInt

Беззнаковые целые (шестнадцатеричные), размер определяется автоматически по количеству цифр.

In [ ]:
x = 0x1              # UInt8 (1 байт)
x = 0x123            # UInt16 (2 байта)
x = 0x1234567        # UInt32 (4 байта)
x = 0x123456789abcdef # UInt64 (8 байт)
x = 0x11112222333344445555666677778888  # UInt128 (16 байт)
Out[0]:
0x11112222333344445555666677778888
In [ ]:
typeof(0xfffffffffffffffffffffffffffffffff)
Out[0]:
BigInt

Julia НЕ проверяет переполнение по умолчанию - циклическое поведение.

In [ ]:
x = typemax(Int64)
Out[0]:
9223372036854775807
In [ ]:
x + 1 # переполнение! 
Out[0]:
-9223372036854775808
In [ ]:
x + 1 == typemin(Int64) 
Out[0]:
true
In [ ]:
x = typemax(UInt64) 
x + 1  
Out[0]:
0x0000000000000000
In [ ]:
x + 1 == typemin(UInt64) 
Out[0]:
true

Решение переполнения через BigInt

In [ ]:
10^19
Out[0]:
-8446744073709551616
In [ ]:
BigInt(10)^19
Out[0]:
10000000000000000000

Числа с плавающей точкой.

In [ ]:
1.0                  # Стандартная запись
1.                   # Можно опустить ноль после точки
0.5                  # Обычная дробь
.5                   # Можно опустить ноль перед точкой
-1.23                # Отрицательное
Out[0]:
-1.23

E-нотация

In [ ]:
1e10                 # 1.0e10 = 1.0 × 10^10
2.5e-4               # 0.00025
Out[0]:
0.00025

Float32 можно задать через суффикс f

In [ ]:
x = 0.5f0
typeof(x)          
Out[0]:
Float32
In [ ]:
2.5f-4
Out[0]:
0.00025f0

Float16 реализовано через явное задание типа

In [ ]:
Float16(4.)
Out[0]:
Float16(4.0)

Битовое представление.

In [ ]:
0.0 == -0.0
Out[0]:
true
In [ ]:
bitstring(0.0)
Out[0]:
"0000000000000000000000000000000000000000000000000000000000000000"
In [ ]:
bitstring(-0.0)  
Out[0]:
"1000000000000000000000000000000000000000000000000000000000000000"

Произвольная точность BigFloat

In [ ]:
BigFloat(2.0)^100 / 4
Out[0]:
3.16912650057057350374175801344e+29

Специальные значения

  • -Inf (отрицательная бесконечность)
  • Inf (положительная бесконечность)
In [ ]:
typemin(Float64)
Out[0]:
-Inf
In [ ]:
typemax(Float64) 
Out[0]:
Inf

Арифметика с бесконечностями

In [ ]:
1 / 0                # Inf
-5 / 0               # -Inf
0 / 0                # NaN 
1 / Inf              # 0.0
1 / -Inf             # -0.0
Inf - Inf            # NaN 
0 * Inf              # NaN
Out[0]:
NaN

NaN не равен сам себе

In [ ]:
NaN == NaN           # false! 
NaN != NaN           # true
NaN < NaN            # false
NaN > NaN            # false
Out[0]:
false

Для корректного сравнения используется isequal

In [ ]:
isequal(NaN, NaN)            # true - NaN считаются равными
Out[0]:
true
In [ ]:
isequal([1 NaN], [1 NaN])    # true - сравнение массивов
Out[0]:
true
In [ ]:
isequal(-0.0, 0.0)           # false - различает знаковые нули!
Out[0]:
false

Машинный эпсилон

In [ ]:
eps(Float64)
Out[0]:
2.220446049250313e-16
In [ ]:
eps(1000.) 
Out[0]:
1.1368683772161603e-13
In [ ]:
eps(0.0) # 5.0e-324 - минимальное положительное
Out[0]:
5.0e-324

Строки и символы

Строки

In [ ]:
s = "Hello!"
Out[0]:
"Hello!"

Интерполяция

In [ ]:
"Интерполяция: $s" 
Out[0]:
"Интерполяция: Hello!"
In [ ]:
"Выражение: $(2 + 2)" 
Out[0]:
"Выражение: 4"

строка (не обрабатывает экранирование)

In [ ]:
"Строка без \n интерполяции"
Out[0]:
"Строка без \n интерполяции"

Многострочные строки

In [ ]:
multiline = """
    Строка
    с переносами
    """
Out[0]:
"Строка\nс переносами\n"

Конкатенация и повторение

In [ ]:
"Hello" * " " * "World" 
Out[0]:
"Hello World"
In [ ]:
"Hi"^3 
Out[0]:
"HiHiHi"

Символы (Char)

In [ ]:
c = 'a' 
typeof(c)                   
Out[0]:
Char
Warning: detected a stack overflow; program state may be corrupted, so further execution might be unreliable.
In [ ]:
c+1
Out[0]:
'b': ASCII/Unicode U+0062 (category Ll: Letter, lowercase)

Символы (Symbol)

In [ ]:
sym = :my_symbol
typeof(sym)
Out[0]:
Symbol

Часто используются, как ключи словарей

In [ ]:
Dict(:key => "value")
Out[0]:
Dict{Symbol, String} with 1 entry:
  :key => "value"

Структуры и абстрактные типы

Используются для создания пользовательских типов и организации иерархии.

In [ ]:
# Неизменяемая структура
struct Point
    x::Float64
    y::Float64
end
In [ ]:
# Изменяемая структура
mutable struct MPoint
    x::Float64
    y::Float64
end
In [ ]:
# Абстрактный тип
abstract type Shape end
struct Circle <: Shape
    radius::Float64
end
struct Rectangle <: Shape
    width::Float64
    height::Float64
end
In [ ]:
# Конструкторы
Point(3.0, 4.0)            # автоматический
Out[0]:
Point(3.0, 4.0)

Коллекции

Кортежи (Tuples) - неизменяемая структура, работает быстрее массивов

In [ ]:
t = (1, 2, 3)
t[1]
Out[0]:
1
In [ ]:
# t[1] = 5 #ERROR

Распаковка кортежей

In [ ]:
a, b, c = t
Out[0]:
(1, 2, 3)

Словари (Dict)

In [ ]:
d = Dict("a" => 1, "b" => 2)
Out[0]:
Dict{String, Int64} with 2 entries:
  "b" => 2
  "a" => 1
In [ ]:
d["a"]
Out[0]:
1
In [ ]:
d["c"] = 3   
Out[0]:
3
In [ ]:
haskey(d, "a")
Out[0]:
true
In [ ]:
keys(d), values(d)
Out[0]:
(["c", "b", "a"], [3, 2, 1])

Множества (Set) - удаляет дубликаты

In [ ]:
s = Set([1, 2, 2, 3]) 
Out[0]:
Set{Int64} with 3 elements:
  2
  3
  1
In [ ]:
1  s 
Out[0]:
true
In [ ]:
push!(s, 4)
Out[0]:
Set{Int64} with 4 elements:
  4
  2
  3
  1

MISSING и NOTHING

nothing — аналог null/None (значение отсутствует)

In [ ]:
function f()
    return nothing
end
f() === nothing
Out[0]:
true

missing — пропущенные данные (распространяются в вычислениях)

In [ ]:
1 + missing
Out[0]:
missing
In [ ]:
# mean([1, missing, 3]) #ERROR

Проверка

In [ ]:
ismissing(missing)      
Out[0]:
true
In [ ]:
isnothing(nothing)     
Out[0]:
true

Базовые операторы

Арифметические операторы

In [ ]:
1 + 10 - 5           # сложение и вычитание
5 * 20 / 10          # умножение и деление
20 \ 10              # обратное деление (10 / 20)
3^3                  # возведение в степень
5.5 % -2             # остаток от деления    
Out[0]:
1.5

Неявное умножение (числовой коэффициент), Julia позволяет писать 2x вместо 2*x

In [ ]:
x = 3
2x                   # неявное умножение
2(x + 1)             # работает и со скобками
2x^2                 # приоритет: ^ выше неявного умножения
2^2x                 
Out[0]:
64

Логические операторы

In [ ]:
!true                # false - отрицание
!false               # true
true && true         # true - логическое И (сокращённое)
true && false        # false
false && false       # false
true || true         # true - логическое ИЛИ (сокращённое)
true || false        # true
false || false       # false
Out[0]:
false

Побитовые операторы

In [ ]:
~100                 # -101 - побитовое НЕ (инверсия)
121 & 232            # 104 - побитовое И
121 | 232            # 249 - побитовое ИЛИ
121  232            # 145 - побитовое XOR (Unicode символ!)
xor(121, 232)        # 145 - то же через функцию
~UInt32(121)         # 0xffffff86 - инверсия с сохранением типа
~UInt8(121)          # 0x86 - результат тоже UInt8
Out[0]:
0x86

Операторы обновления (in-place)

In [ ]:
x = 25
x += 25              # x = x + 25
x *= 2               # x = x * 2
x /= 4               # x = x / 4
x ^= 2               # x = x^2
Out[0]:
625.0

Операторы обновления могут менять тип данных

In [ ]:
x = 0x01             
typeof(x)                     
Out[0]:
UInt8
In [ ]:
x *= 2               
typeof(x)   
Out[0]:
Int64

Векторизация

Создание матрицы

In [ ]:
x = [1 2 3 4 5; 6 7 8 9 10]
Out[0]:
2×5 Matrix{Int64}:
 1  2  3  4   5
 6  7  8  9  10

Оператор . применяет операцию поэлементно, так же этот оператор создаёт новую матрицу.

In [ ]:
x.^2
Out[0]:
2×5 Matrix{Int64}:
  1   4   9  16   25
 36  49  64  81  100
In [ ]:
x .+ 1               # Каждый элемент + 1 
x .- 1               # Каждый элемент - 1
x .* 2               # Каждый элемент * 2
x ./ 2               # Каждый элемент / 2
Out[0]:
2×5 Matrix{Float64}:
 0.5  1.0  1.5  2.0  2.5
 3.0  3.5  4.0  4.5  5.0

Точечные операторы обновления

In [ ]:
x = [1 2 3 4 5; 6 7 8 9 10]
Out[0]:
2×5 Matrix{Int64}:
 1  2  3  4   5
 6  7  8  9  10

Мутирует СУЩЕСТВУЮЩИЙ массив x, перезаписывая его.

In [ ]:
x .+= 1
Out[0]:
2×5 Matrix{Int64}:
 2  3  4   5   6
 7  8  9  10  11

Создаём НОВЫЙ массив, x не меняется

In [ ]:
y = x .+ 1
Out[0]:
2×5 Matrix{Int64}:
 3  4   5   6   7
 8  9  10  11  12

Операторы сравнения

In [ ]:
2 == 2.0             # true - разные типы, но равные значения
3 == 5               # false
3 != 5               # true
3 < 5                # true
5 > 3                # true
3 <= 3               # true
5 >= 5               # true
Out[0]:
true

Особенности для Float:

  • Положительный ноль равен, но не больше отрицательного
  • Inf равен себе и больше всего (кроме NaN)
  • -Inf равен себе и меньше всего (кроме NaN)
  • NaN не сравним ни с чем

Так же Julia позволяет писать математические цепочки сравнений.

In [ ]:
10 < 15 <= 20 < 30 == 30 > 20 >= 10 == 10 < 30 != 5
Out[0]:
true

Эквивалентно:

In [ ]:
(10 < 15) && (15 <= 20) && (20 < 30) && (30 == 30) && (30 > 20) && (20 >= 10) && (10 == 10) && (10 < 30) && (30 != 5)
Out[0]:
true

Точечные сравнения

In [ ]:
x = [1 2 3 4 5; 6 7 8 9 10]
x .<= 3
Out[0]:
2×5 BitMatrix:
 1  1  1  0  0
 0  0  0  0  0
In [ ]:
1 .< x .< 7
Out[0]:
2×5 BitMatrix:
 0  1  1  1  1
 1  0  0  0  0

Индексация

In [ ]:
A = [1 2 3; 4 5 6]          # 2×3 Matrix{Int64}
A[1, 2]                     # 2 (первая строка, второй столбец)
A[1, :]                     # первая строка (1×3)
A[:, 2]                     # второй столбец (2×1)
A[1:2, 1]                   # первый столбец как вектор
A[1:2, 1:2]                 # подматрица 2×2
Out[0]:
2×2 Matrix{Int64}:
 1  2
 4  5

Индексация с end

In [ ]:
A[1, end]                   # последний элемент первой строки
A[end, 1]                   # последний элемент первого столбца
Out[0]:
4

Логическая индексация

In [ ]:
A[A .> 2]                   # [4, 5, 6, 3] (выравнивает в вектор)
Out[0]:
4-element Vector{Int64}:
 4
 5
 3
 6

Приоритет операторов

Иерархия (от высшего к низшему):

  1. Числовые коэффициенты: 2x (выше всех бинарных, кроме ^)
  2. Возведение в степень: ^
  3. Унарные: +x, -x, √x, ...
  4. Умножение/деление: *, /, \, ÷, %
  5. Сложение/вычитание: +, -
  6. Сдвиг: <<, >>, >>>
  7. Побитовое И: &
  8. Побитовое XOR: ⊻
  9. Побитовое ИЛИ: |
  10. Сравнения: ==, !=, <, <=, >, >=
  11. Утверждение типа: <:
  12. Логическое И: &&
  13. Логическое ИЛИ: ||
  14. Оператор pipe: |>
  15. Присваивание: =, +=, -=, и т.д.

Сначала x^2=9, потом 2*9

In [ ]:
x = 3
2x^2
Out[0]:
18

Сначала 2x=6, потом 2^6!

In [ ]:
2^2x                 
Out[0]:
64

Управляющие конструкции

Условный оператор

In [ ]:
x = 10
msg = if x > 5
    "Больше пяти"
else
    "Мало"
end
Out[0]:
"Больше пяти"

Тернарный оператор

In [ ]:
x > 5 ? "Да" : "Нет"
Out[0]:
"Да"

Short-circuit evaluation (замена if)

In [ ]:
x > 0 && println("Положительное")   # Выполнится, если true
x < 0 || println("Не отрицательное") # Выполнится, если первое false
Положительное
Не отрицательное

Циклы:

  • С предусловием
  • С постусловием
In [ ]:
for i in 1:3
    println(i)
end
1
2
3
In [ ]:
while x > 0
    x -= 1
end

Итерация по коллекциям

In [ ]:
for (i, val) in enumerate(["a", "b", "c"])
    println("$i: $val")
end
1: a
2: b
3: c

Генераторы и comprehensions

Array Comprehension (создание массива)

In [ ]:
squares = [x^2 for x in 1:5]
Out[0]:
5-element Vector{Int64}:
  1
  4
  9
 16
 25

С условием

In [ ]:
evens = [x for x in 1:10 if x % 2 == 0]
Out[0]:
5-element Vector{Int64}:
  2
  4
  6
  8
 10

Generator (ленивое вычисление, экономит память), не создаёт массив в памяти, а итерируется по одному элементу.

In [ ]:
gen = (x^2 for x in 1:1000000)
sum(gen)
Out[0]:
333333833333500000

Функции и множественная диспетчеризация

Краткая запись (одно выражение)

In [ ]:
f(x) = x^2 + 1
Out[0]:
f (generic function with 2 methods)
In [ ]:
f(5)
Out[0]:
26
In [ ]:
f(1)
Out[0]:
2

Классическая запись (блок кода)

In [ ]:
function g(x, y)
    return x + y
end
Out[0]:
g (generic function with 1 method)

Аргументы по умолчанию, ключевые аргументы

In [ ]:
function greet(name; greeting="Hello", punctuation="!")
    return "$greeting, $name$punctuation"
end
greet("Engee", greeting="Hi")
Out[0]:
"Hi, Engee!"

Анонимные функции

In [ ]:
h = x -> x^2
h(4) 
Out[0]:
16

Часто используются в map, filter:

In [ ]:
map(x -> x^2, [1, 2, 3])
Out[0]:
3-element Vector{Int64}:
 1
 4
 9

Множественная диспетчеризация (Multiple Dispatch) - одна и та же функция ведёт себя по-разному в зависимости от типов данных

In [ ]:
say_hello(name::String) = "Привет, человек $name"
say_hello(name::Symbol) = "Привет, робот :$name"

say_hello("Анна")
say_hello(:R2D2) 
Out[0]:
"Привет, робот :R2D2"

Запуск программ

image.png

Выполнит файл

In [ ]:
include("example.jl")
Hello, World!

Режиы REPL

Основной режим - julia

In [ ]:
2+2
Out[0]:
4

Справочный режим (?)

In [ ]:
?sqrt
search: sqrt isqrt sort sort! Cshort struct cbrt stat sprint

Out[0]:

?sqrt

Режим пакетов (])

In [ ]:
]add DataFrames
   Resolving package versions...
     Project No packages added to or removed from `~/.project/Project.toml`
    Manifest No packages added to or removed from `~/.project/Manifest.toml`
In [ ]:
]status
Status `~/.project/Project.toml`
  [150eb455] CoordinateTransformations v0.6.4
  [a93c6f00] DataFrames v1.8.1
  [cbc4b850] ImageBinarization v0.3.1
  [4381153b] ImageDraw v0.2.6
  [92ff4b2b] ImageFeatures v0.5.3
  [6a3955dd] ImageFiltering v0.7.12
 [787d08f9] ImageMorphology v0.4.6
  [916415d5] Images v0.26.2
  [033835bb] JLD2 v0.6.3
  [c46f51b8] ProfileView v1.10.3
  [6038ab10] Rotations v1.7.1
 [90137ffa] StaticArrays v1.9.16
  [5e47fb64] TestImages v1.9.0
Info Packages marked with  have new versions available but compatibility constraints restrict them from upgrading. To see why use `status --outdated`

Режим оболочки (;)

In [ ]:
;ls
Language_Features.ngscript
example.jl

Макросы

Макросы позволяют генерировать код и расширять синтаксис; в Julia они часто используются для отладки, измерения времени, оптимизации.

In [ ]:
@show 2 + 2          # выводит выражение и результат
2 + 2 = 4
Out[0]:
4
In [ ]:
@time sleep(0.1)     # измеряет время выполнения
  0.102356 seconds (10.70 k allocations: 1.160 MiB)
In [ ]:
@assert 1 == 1       # проверка условия
In [ ]:
macro hello(expr)
    quote
        println("Привет! Сейчас выполнится:")
        println("  ", $(string(expr)))
        $(esc(expr))
    end
end
@hello 2 + 3 * 5
Привет! Сейчас выполнится:
  2 + 3 * 5
Out[0]:
17

Модули и пространства имён

In [ ]:
module MyModule
    export hello, PI
    const PI = 3.14
    hello() = println("Hello from module")
    # не экспортируется
    internal() = println("internal")
end
Out[0]:
Main.MyModule
In [ ]:
using .MyModule   # или import MyModule
hello()           # доступно
# internal()     # ошибка, не экспортировано
Hello from module

Обработка ошибок

In [ ]:
try
    sqrt(-1)
catch e
    println("Caught an error: $e")
finally
    println("This always runs")
end
Caught an error: DomainError(-1.0, "sqrt was called with a negative real argument but will only return a complex result if called with a complex argument. Try sqrt(Complex(x)).")
This always runs
In [ ]:
# Генерация ошибок
if x < 0
    error("x must be non-negative")
end
In [ ]:
# Пользовательские исключения
struct MyError <: Exception
    msg::String
end
# throw(MyError("something went wrong")) #ERROR

Документирование

In [ ]:
"""
    square(x)

Возвращает квадрат числа `x`.

# Arguments
- `x`: число (любой числовой тип)

# Example
```julia
julia> square(5)
25

"""
square(x) = x^2
Out[0]:
square

Вывод

Вы прошли через концентрированное изложение основ Julia, где основное внимание уделялось не пространным объяснениям, а наглядным фрагментам кода. Такой стиль позволяет не просто прочитать о возможностях языка, а сразу увидеть их в действии, запомнить синтаксис и понять логику работы. Теперь у вас есть компактная шпаргалка, к которой можно возвращаться при написании собственных программ.