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 может его эффективно скомпилировать.
Поддержать проект