LEARN X · ЗА 15 МИН
Julia
Экспресс-тур по Julia: весь язык на одной странице в комментариях кода — типы, массивы, broadcasting, multiple dispatch, struct и работа с данными.
Julia — язык для научных и численных вычислений: динамичный и удобный как Python, но быстрый как C. Главная фишка — множественная диспетчеризация. Здесь весь язык на одной странице: читай код сверху вниз, всё объяснено в комментариях.
Вывод и комментарии
# Это однострочный комментарий
#= Это многострочный
комментарий =#
println("Привет, Julia!") # печатает строку и перевод строки
print("без ") # печатает без перевода строки
print("переноса\n") # \n — перенос вручную
# @show удобен для отладки — печатает выражение и его значение
@show 2 + 2 # 2 + 2 = 4
# display — «красивый» вывод для сложных объектов
display([1, 2, 3]) # 3-element Vector{Int64}: ...
Переменные и типы
Типизация динамическая, но можно добавлять аннотации ::Тип. Имена переменных поддерживают юникод.
x = 42 # Int64 (на 64-битной системе)
y = 3.14 # Float64
name = "Юлия" # String
flag = true # Bool
# Аннотация типа: значение приводится к указанному типу
z::Int = 7 # z всегда Int
# typeof — узнать тип значения
typeof(x) # Int64
typeof(y) # Float64
typeof(flag) # Bool
# Юникод-имена: набираются через \alpha + Tab и т.п.
α = 0.5 # \alpha
π # встроенная π = 3.141592653589793
# Целые произвольной точности и рациональные числа
big(2)^100 # 1267650600228229401496703205376
3 // 4 # 3//4 — тип Rational{Int64}
# Спецзначения
nothing # аналог None/null
missing # пропущенное значение (для данных)
Строки
s = "Julia"
# Интерполяция через $
println("Язык: $s") # Язык: Julia
println("2 + 2 = $(2 + 2)") # 2 + 2 = 4 — выражение в $( )
# Конкатенация
"abc" * "def" # "abcdef" — склейка через *
"ха" ^ 3 # "хахаха" — повтор через ^
# Методы строк
length(s) # 5
uppercase(s) # "JULIA"
lowercase(s) # "julia"
occursin("ul", s) # true — содержит подстроку
replace(s, "a" => "A") # "JuliA"
split("a,b,c", ",") # ["a", "b", "c"]
join(["a","b"], "-") # "a-b"
# Индексация (с 1!) и срезы
s[1] # 'J' — символ Char
s[1:3] # "Jul" — срез
strip(" hi ") # "hi"
# Многострочные строки
text = """
первая строка
вторая строка
"""
Операторы и условия
# Арифметика
10 + 3 # 13
10 - 3 # 7
10 * 3 # 30
10 / 3 # 3.333... — деление всегда Float
10 ÷ 3 # 3 — целочисленное деление (\div)
10 % 3 # 1 — остаток
2 ^ 10 # 1024 — степень
# Сравнения возвращают Bool
3 < 5 # true
3 >= 5 # false
3 == 3.0 # true — равенство значений
3 != 4 # true
# Цепочки сравнений работают «как в математике»
1 < 2 < 3 # true
# if / elseif / else ... end
n = 7
if n > 0
println("положительное")
elseif n < 0
println("отрицательное")
else
println("ноль")
end
# Тернарный оператор
max_val = 3 > 5 ? 3 : 5 # 5
# Короткое замыкание && и ||
true && println("выполнится")
false || println("тоже выполнится")
Циклы
# Диапазон 1:5 включает оба конца
for i in 1:5
print(i, " ") # 1 2 3 4 5
end
println()
# Диапазон с шагом: начало:шаг:конец
for i in 0:2:10
print(i, " ") # 0 2 4 6 8 10
end
println()
# while
n = 3
while n > 0
println(n) # 3, 2, 1
global n -= 1 # global нужен в скрипте на верхнем уровне
end
# Перебор коллекции
for fruit in ["яблоко", "груша"]
println(fruit)
end
# enumerate — индекс + значение
for (i, v) in enumerate(["a", "b"])
println("$i: $v") # 1: a / 2: b
end
# break и continue работают как обычно
for i in 1:100
i == 3 && continue # пропустить 3
i > 5 && break # выйти после 5
end
Массивы и матрицы
Массивы — основа Julia. Индексация с 1. Точка перед оператором (.) — это broadcasting: применить операцию поэлементно.
# Вектор (1D массив)
v = [10, 20, 30]
v[1] # 10 — первый элемент
v[end] # 30 — последний
push!(v, 40) # добавить в конец -> [10,20,30,40]
pop!(v) # удалить последний -> 40
length(v) # 3
# Матрица: пробел = столбцы, ; = строки
M = [1 2 3;
4 5 6]
size(M) # (2, 3) — 2 строки, 3 столбца
M[1, 2] # 2 — строка 1, столбец 2
M[:, 1] # [1, 4] — весь первый столбец
M[2, :] # [4, 5, 6] — вся вторая строка
transpose(M) # транспонирование
# Broadcasting — поэлементные операции через точку
a = [1, 2, 3]
a .+ 10 # [11, 12, 13]
a .^ 2 # [1, 4, 9]
sqrt.(a) # [1.0, 1.41, 1.73] — функция к каждому элементу
a .* a # [1, 4, 9] — поэлементное умножение
# Comprehension — генератор массива
squares = [i^2 for i in 1:5] # [1, 4, 9, 16, 25]
even = [i for i in 1:10 if i % 2 == 0] # [2, 4, 6, 8, 10]
# Полезное
zeros(3) # [0.0, 0.0, 0.0]
ones(2, 2) # матрица 2x2 из единиц
collect(1:5) # [1, 2, 3, 4, 5] — диапазон в массив
Кортежи, словари, множества
# Tuple — неизменяемый, фиксированной длины
t = (1, "два", 3.0)
t[1] # 1
# Распаковка
a, b, c = t
# Named tuple — кортеж с именованными полями
nt = (x = 10, y = 20)
nt.x # 10
# Dict — словарь ключ => значение
d = Dict("a" => 1, "b" => 2)
d["a"] # 1
d["c"] = 3 # добавить пару
haskey(d, "a") # true
get(d, "z", 0) # 0 — значение по умолчанию
keys(d) # ключи
values(d) # значения
for (k, v) in d
println("$k => $v")
end
# Set — множество уникальных значений
s = Set([1, 2, 2, 3]) # Set(1, 2, 3)
push!(s, 4)
in(2, s) # true
union(Set([1,2]), Set([2,3])) # Set(1,2,3)
intersect(Set([1,2]), Set([2,3])) # Set(2)
Функции
# Полная форма: function ... end
function add(a, b)
return a + b # return можно опустить — вернётся последнее выражение
end
add(2, 3) # 5
# Краткая форма «присваиванием»
square(x) = x^2
square(4) # 16
# Анонимные функции через ->
double = x -> x * 2
double(5) # 10
# Значения по умолчанию и именованные аргументы (после ;)
greet(name, greeting="Привет"; loud=false) = loud ? uppercase("$greeting, $name") : "$greeting, $name"
greet("Аня") # "Привет, Аня"
greet("Аня"; loud=true) # "ПРИВЕТ, АНЯ"
# Переменное число аргументов через ...
mysum(args...) = reduce(+, args)
mysum(1, 2, 3, 4) # 10
# По соглашению функции с ! изменяют аргумент
sort!([3, 1, 2]) # массив отсортирован на месте
Множественная диспетчеризация
Главная идея Julia: какой метод вызвать, решается по типам всех аргументов, а не только первого. Это даёт расширяемость и скорость.
# Одно имя — много методов под разные типы
collide(a::Int, b::Int) = "два числа: $(a + b)"
collide(a::String, b::String) = "две строки: $a$b"
collide(a::Int, b::String) = "смесь: $a и $b"
collide(2, 3) # "два числа: 5"
collide("a", "b") # "две строки: ab"
collide(1, "кот") # "смесь: 1 и кот"
# Julia выбирает самый специфичный подходящий метод.
# ::Any — самый общий, ::Int — более конкретный.
area(shape) = "неизвестная фигура" # ловит всё (Any)
area(r::Float64) = "круг радиуса $r" # конкретнее
# methods показывает все методы функции
methods(collide) # 3 method(s) for generic function collide
# Так устроена вся стандартная библиотека: + для Int, Float,
# матриц и т.д. — это разные методы одной функции.
Типы
# struct — составной тип (по умолчанию неизменяемый)
struct Point
x::Float64
y::Float64
end
p = Point(1.0, 2.0)
p.x # 1.0
# mutable struct — поля можно менять
mutable struct Counter
value::Int
end
c = Counter(0)
c.value += 1 # теперь 1
# Абстрактные типы образуют иерархию (<: означает «подтип»)
abstract type Animal end
struct Dog <: Animal end
struct Cat <: Animal end
# Метод для всех Animal сразу — через абстрактный тип
speak(a::Animal) = "какой-то звук"
speak(d::Dog) = "Гав"
speak(Dog()) # "Гав"
speak(Cat()) # "какой-то звук" — попал в общий метод
# Параметрические типы: T — параметр-тип
struct Box{T}
content::T
end
Box(42) # Box{Int64}
Box("hi") # Box{String}
Dog <: Animal # true — проверка подтипа
Работа с данными
data = [3, 1, 4, 1, 5, 9, 2, 6]
sum(data) # 31 — сумма
minimum(data) # 1
maximum(data) # 9
length(data) # 8
# Статистика (sum/length встроены; mean — из Statistics)
sum(data) / length(data) # 3.875 — среднее вручную
# Сортировка
sort(data) # [1, 1, 2, 3, 4, 5, 6, 9]
sort(data, rev=true) # по убыванию
# map — применить функцию к каждому элементу
map(x -> x^2, [1, 2, 3]) # [1, 4, 9]
# filter — оставить подходящие
filter(x -> x > 3, data) # [4, 5, 9, 6]
# reduce — свернуть в одно значение
reduce(+, [1, 2, 3, 4]) # 10
# Цепочка через |> (pipe) и точку как broadcasting
[1, 2, 3, 4] |> sum # 10
# Удобные сокращения
count(x -> x > 3, data) # 3 — сколько элементов > 3
any(x -> x > 8, data) # true — есть ли хоть один
all(x -> x > 0, data) # true — все ли подходят
unique([1, 1, 2, 3]) # [1, 2, 3]
Производительность и пакеты
Julia компилируется в нативный код «на лету» (JIT) — поэтому при правильном стиле скорость близка к C.
# Подключение пакета/модуля
using Statistics # mean, median, std из стандартной библиотеки
mean([1, 2, 3, 4]) # 2.5
std([1, 2, 3, 4]) # стандартное отклонение
# Установка пакетов — через встроенный менеджер Pkg
# import Pkg; Pkg.add("DataFrames")
# Broadcasting (точка) — главный приём для скорости:
# одна операция применяется ко всему массиву без циклов
v = collect(1:1_000_000)
result = v .* 2 .+ 1 # быстро и поэлементно, без явного for
# Аннотации типов в «горячих» функциях помогают компилятору
function fast_sum(x::Vector{Float64})
total = 0.0
for v in x
total += v
end
return total
end
# @time — замер времени и аллокаций
# @time fast_sum(rand(10^6))
# Совет: пиши код в функциях (не на верхнем уровне) —
# так Julia может его эффективно скомпилировать.