Julia: Возможности языка
Данная статья представляет собой сжатый справочник по основам языка Julia, построенный по принципу «минимум теории — максимум практики». Весь материал организован вокруг кода: каждый раздел иллюстрируется непосредственно работающими примерами, а текстовые пояснения сведены к необходимому минимуму. Такой подход позволяет быстро погрузиться в синтаксис, увидеть типичные конструкции и сразу начать экспериментировать, не отвлекаясь на длинные описания. Julia славится своей выразительностью и скоростью, и лучше всего знакомиться с ней через живой код — именно эту возможность даёт предлагаемая подборка. Здесь вы найдёте примеры работы с переменными, типами данных, коллекциями, функциями, множественной диспетчеризацией и другими ключевыми возможностями языка.
Переменные и присваивание
Присвоение целого числа
x = 100
Использование переменной в выражении
x * 5
Переназначение переменной
x = 10 + 10
Julia - динамически типизированный язык, переменная может менять тип
x = "Hello World!"
Присваивание возвращает значение правой части
b=4
a = (b = 2 + 2) * 5
Возможны так же цепочки присваиваний, а примере ниже всем трём переменным присвоено 10
x = y = z = 10
Поддерживается Unicode в именах переменных
х1 = 100
● = "Точка"
δ = 0.001
Регистрозависимость, myvar и MyVar - это две разные переменные.
myvar = 1
MyVar = 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
Поведение с мутабельными типами (массивы)
Важно: присваивание НЕ создаёт копию для мутабельных объектов!
a = [1, 2, 3]
b ссылается на ТОТ ЖЕ массив, что и a
b = a
Мутируем массив через a и выводим b, b тоже видит изменение!
a[1] = 42
b
Перепривязка массива
a = [1, 2, 3]
b = a
a = 3.14159
b
a = [1, 2, 3]
b .= a
a = [1, 2, 30]
b
Типы данных и аннотации
Автоматическое определение типа, Float64 - тип по умолчанию для чисел с плавающей точкой
x = -2.0
typeof(x)
Int64 - тип по умолчанию для целых чисел
x = 2
typeof(x)
x = -2
typeof(x)
Ошибка домена без комплексных чисел
# sqrt(x) #ERROR
Явное указание комплексного типа. Добавляем 0im для комплексного типа
x = -2.0 + 0im
typeof(x)
sqrt(x)
Оператор аннотации типа :: , проверка утверждение "является экземпляром" заданного типа или нет.
(2 + 2)::Int
# (2 + 2)::AbstractFloat #ERROR
(2.0 + 2.0)::AbstractFloat
Проверка стабильности типов
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]) # ассемблерный код
Объявление типа переменной
x = Complex(-2)
typeof(x)
sqrt(x)
Иерархия типов:
- Int64 <: Integer <: Real <: Number <: Any
- Float64 <: AbstractFloat <: Real <: Number <: Any
Параметрические типы
Двумерный массив Int64
Array{Int64, 2}
Вектор Float64 (одномерный)
Array{Float64, 1}
Целые числа
Псевдонимы системных типов:
Int - Int64 целое
UInt - UInt64 беззнаковое целое
10
Ведущий ноль не делает число восьмеричным
0123456789
Поддерживается автоматическое расширение битности для больших чисел
typeof(10000000000000000000)
typeof(1000000000000000000000000000000000000000)
Беззнаковые целые (шестнадцатеричные), размер определяется автоматически по количеству цифр.
x = 0x1 # UInt8 (1 байт)
x = 0x123 # UInt16 (2 байта)
x = 0x1234567 # UInt32 (4 байта)
x = 0x123456789abcdef # UInt64 (8 байт)
x = 0x11112222333344445555666677778888 # UInt128 (16 байт)
typeof(0xfffffffffffffffffffffffffffffffff)
Julia НЕ проверяет переполнение по умолчанию - циклическое поведение.
x = typemax(Int64)
x + 1 # переполнение!
x + 1 == typemin(Int64)
x = typemax(UInt64)
x + 1
x + 1 == typemin(UInt64)
Решение переполнения через BigInt
10^19
BigInt(10)^19
Числа с плавающей точкой.
1.0 # Стандартная запись
1. # Можно опустить ноль после точки
0.5 # Обычная дробь
.5 # Можно опустить ноль перед точкой
-1.23 # Отрицательное
E-нотация
1e10 # 1.0e10 = 1.0 × 10^10
2.5e-4 # 0.00025
Float32 можно задать через суффикс f
x = 0.5f0
typeof(x)
2.5f-4
Float16 реализовано через явное задание типа
Float16(4.)
Битовое представление.
0.0 == -0.0
bitstring(0.0)
bitstring(-0.0)
Произвольная точность BigFloat
BigFloat(2.0)^100 / 4
Специальные значения
- -Inf (отрицательная бесконечность)
- Inf (положительная бесконечность)
typemin(Float64)
typemax(Float64)
Арифметика с бесконечностями
1 / 0 # Inf
-5 / 0 # -Inf
0 / 0 # NaN
1 / Inf # 0.0
1 / -Inf # -0.0
Inf - Inf # NaN
0 * Inf # NaN
NaN не равен сам себе
NaN == NaN # false!
NaN != NaN # true
NaN < NaN # false
NaN > NaN # false
Для корректного сравнения используется isequal
isequal(NaN, NaN) # true - NaN считаются равными
isequal([1 NaN], [1 NaN]) # true - сравнение массивов
isequal(-0.0, 0.0) # false - различает знаковые нули!
Машинный эпсилон
eps(Float64)
eps(1000.)
eps(0.0) # 5.0e-324 - минимальное положительное
Строки и символы
Строки
s = "Hello!"
Интерполяция
"Интерполяция: $s"
"Выражение: $(2 + 2)"
строка (не обрабатывает экранирование)
"Строка без \n интерполяции"
Многострочные строки
multiline = """
Строка
с переносами
"""
Конкатенация и повторение
"Hello" * " " * "World"
"Hi"^3
Символы (Char)
c = 'a'
typeof(c)
c+1
Символы (Symbol)
sym = :my_symbol
typeof(sym)
Часто используются, как ключи словарей
Dict(:key => "value")
Структуры и абстрактные типы
Используются для создания пользовательских типов и организации иерархии.
# Неизменяемая структура
struct Point
x::Float64
y::Float64
end
# Изменяемая структура
mutable struct MPoint
x::Float64
y::Float64
end
# Абстрактный тип
abstract type Shape end
struct Circle <: Shape
radius::Float64
end
struct Rectangle <: Shape
width::Float64
height::Float64
end
# Конструкторы
Point(3.0, 4.0) # автоматический
Коллекции
Кортежи (Tuples) - неизменяемая структура, работает быстрее массивов
t = (1, 2, 3)
t[1]
# t[1] = 5 #ERROR
Распаковка кортежей
a, b, c = t
Словари (Dict)
d = Dict("a" => 1, "b" => 2)
d["a"]
d["c"] = 3
haskey(d, "a")
keys(d), values(d)
Множества (Set) - удаляет дубликаты
s = Set([1, 2, 2, 3])
1 ∈ s
push!(s, 4)
MISSING и NOTHING
nothing — аналог null/None (значение отсутствует)
function f()
return nothing
end
f() === nothing
missing — пропущенные данные (распространяются в вычислениях)
1 + missing
# mean([1, missing, 3]) #ERROR
Проверка
ismissing(missing)
isnothing(nothing)
Базовые операторы
Арифметические операторы
1 + 10 - 5 # сложение и вычитание
5 * 20 / 10 # умножение и деление
20 \ 10 # обратное деление (10 / 20)
3^3 # возведение в степень
5.5 % -2 # остаток от деления
Неявное умножение (числовой коэффициент), Julia позволяет писать 2x вместо 2*x
x = 3
2x # неявное умножение
2(x + 1) # работает и со скобками
2x^2 # приоритет: ^ выше неявного умножения
2^2x
Логические операторы
!true # false - отрицание
!false # true
true && true # true - логическое И (сокращённое)
true && false # false
false && false # false
true || true # true - логическое ИЛИ (сокращённое)
true || false # true
false || false # false
Побитовые операторы
~100 # -101 - побитовое НЕ (инверсия)
121 & 232 # 104 - побитовое И
121 | 232 # 249 - побитовое ИЛИ
121 ⊻ 232 # 145 - побитовое XOR (Unicode символ!)
xor(121, 232) # 145 - то же через функцию
~UInt32(121) # 0xffffff86 - инверсия с сохранением типа
~UInt8(121) # 0x86 - результат тоже UInt8
Операторы обновления (in-place)
x = 25
x += 25 # x = x + 25
x *= 2 # x = x * 2
x /= 4 # x = x / 4
x ^= 2 # x = x^2
Операторы обновления могут менять тип данных
x = 0x01
typeof(x)
x *= 2
typeof(x)
Векторизация
Создание матрицы
x = [1 2 3 4 5; 6 7 8 9 10]
Оператор . применяет операцию поэлементно, так же этот оператор создаёт новую матрицу.
x.^2
x .+ 1 # Каждый элемент + 1
x .- 1 # Каждый элемент - 1
x .* 2 # Каждый элемент * 2
x ./ 2 # Каждый элемент / 2
Точечные операторы обновления
x = [1 2 3 4 5; 6 7 8 9 10]
Мутирует СУЩЕСТВУЮЩИЙ массив x, перезаписывая его.
x .+= 1
Создаём НОВЫЙ массив, x не меняется
y = x .+ 1
Операторы сравнения
2 == 2.0 # true - разные типы, но равные значения
3 == 5 # false
3 != 5 # true
3 < 5 # true
5 > 3 # true
3 <= 3 # true
5 >= 5 # true
Особенности для Float:
- Положительный ноль равен, но не больше отрицательного
- Inf равен себе и больше всего (кроме NaN)
- -Inf равен себе и меньше всего (кроме NaN)
- NaN не сравним ни с чем
Так же Julia позволяет писать математические цепочки сравнений.
10 < 15 <= 20 < 30 == 30 > 20 >= 10 == 10 < 30 != 5
Эквивалентно:
(10 < 15) && (15 <= 20) && (20 < 30) && (30 == 30) && (30 > 20) && (20 >= 10) && (10 == 10) && (10 < 30) && (30 != 5)
Точечные сравнения
x = [1 2 3 4 5; 6 7 8 9 10]
x .<= 3
1 .< x .< 7
Индексация
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
Индексация с end
A[1, end] # последний элемент первой строки
A[end, 1] # последний элемент первого столбца
Логическая индексация
A[A .> 2] # [4, 5, 6, 3] (выравнивает в вектор)
Приоритет операторов
Иерархия (от высшего к низшему):
- Числовые коэффициенты: 2x (выше всех бинарных, кроме ^)
- Возведение в степень: ^
- Унарные: +x, -x, √x, ...
- Умножение/деление: *, /, \, ÷, %
- Сложение/вычитание: +, -
- Сдвиг: <<, >>, >>>
- Побитовое И: &
- Побитовое XOR: ⊻
- Побитовое ИЛИ: |
- Сравнения: ==, !=, <, <=, >, >=
- Утверждение типа: <:
- Логическое И: &&
- Логическое ИЛИ: ||
- Оператор pipe: |>
- Присваивание: =, +=, -=, и т.д.
Сначала x^2=9, потом 2*9
x = 3
2x^2
Сначала 2x=6, потом 2^6!
2^2x
Управляющие конструкции
Условный оператор
x = 10
msg = if x > 5
"Больше пяти"
else
"Мало"
end
Тернарный оператор
x > 5 ? "Да" : "Нет"
Short-circuit evaluation (замена if)
x > 0 && println("Положительное") # Выполнится, если true
x < 0 || println("Не отрицательное") # Выполнится, если первое false
Циклы:
- С предусловием
- С постусловием
for i in 1:3
println(i)
end
while x > 0
x -= 1
end
Итерация по коллекциям
for (i, val) in enumerate(["a", "b", "c"])
println("$i: $val")
end
Генераторы и comprehensions
Array Comprehension (создание массива)
squares = [x^2 for x in 1:5]
С условием
evens = [x for x in 1:10 if x % 2 == 0]
Generator (ленивое вычисление, экономит память), не создаёт массив в памяти, а итерируется по одному элементу.
gen = (x^2 for x in 1:1000000)
sum(gen)
Функции и множественная диспетчеризация
Краткая запись (одно выражение)
f(x) = x^2 + 1
f(5)
f(1)
Классическая запись (блок кода)
function g(x, y)
return x + y
end
Аргументы по умолчанию, ключевые аргументы
function greet(name; greeting="Hello", punctuation="!")
return "$greeting, $name$punctuation"
end
greet("Engee", greeting="Hi")
Анонимные функции
h = x -> x^2
h(4)
Часто используются в map, filter:
map(x -> x^2, [1, 2, 3])
Множественная диспетчеризация (Multiple Dispatch) - одна и та же функция ведёт себя по-разному в зависимости от типов данных
say_hello(name::String) = "Привет, человек $name"
say_hello(name::Symbol) = "Привет, робот :$name"
say_hello("Анна")
say_hello(:R2D2)
Запуск программ
.png)
Выполнит файл
include("example.jl")
Режиы REPL
Основной режим - julia
2+2
Справочный режим (?)
?sqrt
Режим пакетов (])
]add DataFrames
]status
Режим оболочки (;)
;ls
Макросы
Макросы позволяют генерировать код и расширять синтаксис; в Julia они часто используются для отладки, измерения времени, оптимизации.
@show 2 + 2 # выводит выражение и результат
@time sleep(0.1) # измеряет время выполнения
@assert 1 == 1 # проверка условия
macro hello(expr)
quote
println("Привет! Сейчас выполнится:")
println(" ", $(string(expr)))
$(esc(expr))
end
end
@hello 2 + 3 * 5
Модули и пространства имён
module MyModule
export hello, PI
const PI = 3.14
hello() = println("Hello from module")
# не экспортируется
internal() = println("internal")
end
using .MyModule # или import MyModule
hello() # доступно
# internal() # ошибка, не экспортировано
Обработка ошибок
try
sqrt(-1)
catch e
println("Caught an error: $e")
finally
println("This always runs")
end
# Генерация ошибок
if x < 0
error("x must be non-negative")
end
# Пользовательские исключения
struct MyError <: Exception
msg::String
end
# throw(MyError("something went wrong")) #ERROR
Документирование
"""
square(x)
Возвращает квадрат числа `x`.
# Arguments
- `x`: число (любой числовой тип)
# Example
```julia
julia> square(5)
25
"""
square(x) = x^2
Вывод
Вы прошли через концентрированное изложение основ Julia, где основное внимание уделялось не пространным объяснениям, а наглядным фрагментам кода. Такой стиль позволяет не просто прочитать о возможностях языка, а сразу увидеть их в действии, запомнить синтаксис и понять логику работы. Теперь у вас есть компактная шпаргалка, к которой можно возвращаться при написании собственных программ.