Nim
Nim за 15 минут: весь язык на одной странице через закомментированный код — переменные, циклы, seq, proc, object, дженерики, ref/ptr, итераторы.
Nim — компилируемый язык с Python-подобным синтаксисом и скоростью C. Статическая типизация с выводом типов, отступы вместо скобок, метапрограммирование. Ниже весь язык на одной странице: читаем код сверху вниз, всё объяснено в комментариях.
1. Вывод и комментарии
# Однострочный комментарий начинается с #
echo "Привет, Nim!" # echo печатает строку и перевод строки
#[
Это многострочный (блочный) комментарий.
Он может занимать несколько строк
и даже #[ вкладываться ]# внутрь себя.
]#
echo 2, " ", 3 # echo принимает несколько аргументов: 2 3
echo "a", "b", "c" # склеит без пробелов: abc
## Документационный комментарий (две решётки) — попадает в docs.
2. Переменные
Три способа объявления: var (изменяемая), let (неизменяемая после присваивания), const (вычисляется при компиляции).
var x = 10 # var — изменяемая переменная, тип int выведен автоматически
x = 20 # можно переприсвоить
let y = 3.14 # let — связывается один раз, дальше менять нельзя (тип float)
# y = 1.0 # ОШИБКА компиляции: 'y' нельзя изменить
const PI = 3.14159 # const — известна на этапе компиляции (только из const-выражений)
# Явное указание типа: имя: Тип = значение
var age: int = 30
var price: float = 9.99
var ok: bool = true # bool: true / false
var name: string = "Анна" # string — строка (UTF-8)
var letter: char = 'A' # char — один байт, в одинарных кавычках
# Объявление без значения — получит "нулевое" значение типа
var counter: int # counter == 0
var flag: bool # flag == false
# Несколько переменных сразу
var
a = 1
b = 2
c = 3
echo a + b + c # 6
3. Строки
import std/strutils # модуль со строковыми утилитами
let first = "Иван"
let last = "Петров"
# Конкатенация оператором &
let full = first & " " & last
echo full # Иван Петров
# Интерполяция строк через strformat
import std/strformat
let n = 5
echo fmt"Число: {n}, квадрат: {n*n}" # Число: 5, квадрат: 25
# Многострочный строковый литерал в тройных кавычках
let text = """
Первая строка
Вторая строка
"""
# Полезные методы (вызываются как процедуры или через точку — см. секцию 8)
echo " hello ".strip() # "hello" — убирает пробелы по краям
echo toUpperAscii("abc") # ABC
echo "abc".toUpperAscii() # ABC — то же через точку (UFCS, см. секцию 8)
echo "a,b,c".split(",") # @["a", "b", "c"]
echo "hello".len # 5 — длина строки
echo "hello"[0] # 'h' — индексация по символам
echo "hello"[1..3] # "ell" — срез (slice)
echo "hi".repeat(3) # hihihi
echo "Nim".contains("i") # true
4. Операторы и условия
# Арифметика
echo 7 + 3 # 10
echo 7 - 3 # 4
echo 7 * 3 # 21
echo 7 div 3 # 2 — целочисленное деление (ключевое слово div)
echo 7 mod 3 # 1 — остаток (mod)
echo 7 / 3 # 2.3333... — / всегда даёт float
echo 2 ^ 10 # 1024 — возведение в степень (нужен import std/math)
# Сравнение возвращает bool: == != < > <= >=
echo 5 > 3 # true
echo 5 == 5 # true
echo 5 != 4 # true
# Логические: and / or / not
echo (true and false) # false
echo (true or false) # true
echo not true # false
# if / elif / else — ветвление по отступам
let score = 75
if score >= 90:
echo "Отлично"
elif score >= 60:
echo "Норма" # сработает эта ветка
else:
echo "Плохо"
# if — это выражение, можно присвоить результат
let sign = if score >= 0: "плюс" else: "минус"
echo sign # плюс
# case — выбор по значению (аналог switch)
let day = 3
case day
of 1, 2, 3, 4, 5:
echo "Будни" # сработает
of 6, 7:
echo "Выходные"
else:
echo "Нет такого дня"
# when — "if на этапе компиляции" (по условиям, известным компилятору)
when defined(windows):
echo "Сборка под Windows"
else:
echo "Не Windows"
5. Циклы
# for с диапазоном: a..b включает оба конца
for i in 0..3:
echo i # 0 1 2 3 (по строкам)
# a..<b — верхняя граница НЕ включается
for i in 0..<3:
echo i # 0 1 2
# Шаг через countup / countdown
for i in countup(0, 10, 2):
echo i # 0 2 4 6 8 10
# for-in по коллекции
for fruit in ["яблоко", "груша"]:
echo fruit
# pairs — индекс и значение сразу
for idx, val in ["a", "b", "c"]:
echo idx, " -> ", val # 0 -> a ; 1 -> b ; 2 -> c
# while — пока условие истинно
var k = 0
while k < 3:
echo "k=", k
inc k # inc увеличивает на 1 (есть и dec)
# break — выйти из цикла, continue — к следующей итерации
for i in 0..10:
if i == 2: continue # пропустить 2
if i == 5: break # остановиться на 5
echo i # 0 1 3 4
6. Коллекции
array — фиксированный размер, seq — динамический, Table — словарь, set / HashSet — множества.
# Массив (array) — размер фиксирован и известен при компиляции
var arr: array[3, int] = [10, 20, 30]
echo arr[0] # 10
arr[1] = 99
echo arr # [10, 99, 30]
echo arr.len # 3
# Последовательность (seq) — динамический массив
var nums = @[1, 2, 3] # @[...] создаёт seq
nums.add(4) # добавить элемент
echo nums # @[1, 2, 3, 4]
echo nums.len # 4
nums.delete(0) # удалить по индексу
echo nums # @[2, 3, 4]
# Перебор и преобразования (нужен import std/sequtils)
import std/sequtils
let doubled = @[1, 2, 3].map(proc(x: int): int = x * 2)
echo doubled # @[2, 4, 6]
echo @[1, 2, 3, 4].filter(proc(x: int): bool = x mod 2 == 0) # @[2, 4]
# Таблица (Table) — пары ключ-значение
import std/tables
var ages = {"Анна": 30, "Борис": 25}.toTable
echo ages["Анна"] # 30
ages["Вера"] = 40 # добавить/изменить
echo ages.hasKey("Борис") # true
for name, age in ages:
echo name, ": ", age
# Множество (set) — для перечислимых/малых типов
var letters: set[char] = {'a', 'b', 'c'}
echo 'b' in letters # true
letters.incl('d') # добавить
letters.excl('a') # удалить
# HashSet — множество произвольных значений
import std/sets
var s = toHashSet([1, 2, 2, 3])
echo s.len # 3 — дубликаты схлопнулись
7. Процедуры и функции
proc — обычная процедура, func — гарантированно без побочных эффектов (чистая функция).
# proc имя(параметры): ТипРезультата = тело
proc add(a: int, b: int): int =
return a + b
echo add(2, 3) # 5
# Последнее выражение и так становится результатом — return не обязателен
proc square(x: int): int =
x * x
echo square(4) # 16
# Параметры по умолчанию
proc greet(name: string, greeting: string = "Привет"): string =
greeting & ", " & name
echo greet("Аня") # Привет, Аня
echo greet("Аня", "Здарова") # Здарова, Аня
# Неявная переменная result — это "возвращаемое значение", доступна сразу
proc sumTo(n: int): int =
for i in 1..n:
result += i # result уже инициализирован нулём
# тело само вернёт result
echo sumTo(5) # 15
# func — чистая функция: компилятор запрещает побочные эффекты (echo, var-глобалки)
func cube(x: int): int =
x * x * x
echo cube(3) # 27
# var-параметр передаётся по ссылке — функция может его менять
proc double(x: var int) =
x = x * 2
var value = 21
double(value)
echo value # 42
8. Синтаксис вызовов (UFCS)
Uniform Function Call Syntax: первый аргумент можно вынести "перед точкой". len(x) и x.len — одно и то же.
# Эти две строки полностью эквивалентны:
echo len("hello") # 5
echo "hello".len # 5
# Любую proc можно звать в "методном" стиле
proc square(x: int): int = x * x
echo square(5) # 25
echo 5.square # 25 — то же самое
# Цепочки вызовов читаются слева направо
import std/strutils
echo " Hello World ".strip().toLowerAscii().split(" ")
# @["hello", "world"]
# Скобки можно опускать, если нет неоднозначности
echo "hello".len # без скобок у len
# Поэтому в Nim нет разницы между "методами" и "функциями" —
# x.foo(y) == foo(x, y). Это и есть UFCS.
9. Типы: object, наследование, enum, tuple
# object — структура с именованными полями
type
Point = object
x: int
y: int
var p = Point(x: 3, y: 4)
echo p.x, ", ", p.y # 3, 4
p.x = 10 # поля изменяемы, если переменная var
# Наследование: object of Родитель + ref для полиморфизма
type
Animal = ref object of RootObj
name: string
Dog = ref object of Animal
breed: string
let d = Dog(name: "Рекс", breed: "лайка")
echo d.name # Рекс — поле унаследовано от Animal
# enum — перечисление
type
Color = enum
Red, Green, Blue
var c = Green
echo c # Green
echo ord(Blue) # 2 — порядковый номер
# tuple — кортеж: набор полей, можно безымянных
var person = (name: "Аня", age: 30)
echo person.name # Аня
echo person.age # 30
let pair = (1, "один") # безымянный кортеж
echo pair[0], " ", pair[1] # 1 один
# Распаковка кортежа
let (nm, ag) = ("Боря", 25)
echo nm, " ", ag # Боря 25
10. Дженерики и шаблоны
Дженерики параметризуют код типом [T]. Шаблоны (template) — подстановка кода на этапе компиляции.
# Дженерик: [T] — параметр-тип, подставляется при вызове
proc maxOf[T](a: T, b: T): T =
if a > b: a else: b
echo maxOf[int](3, 7) # 7
echo maxOf(2.5, 1.5) # 2.5 — T выведен автоматически
echo maxOf("abc", "xyz") # xyz — работает и со строками
# Дженерик-тип: контейнер любого типа
type
Box[T] = object
value: T
var b = Box[string](value: "привет")
echo b.value # привет
# template — мини-макрос: тело подставляется в место вызова
template twice(body: untyped) =
body
body
twice:
echo "эхо" # напечатает "эхо" дважды
# template как короткий синоним выражения
template isEven(n: int): bool = n mod 2 == 0
echo isEven(4) # true
11. Управление памятью и указатели
Nim управляет памятью автоматически (ARC/ORC). ref — безопасная управляемая ссылка, ptr — сырой указатель (для FFI и низкого уровня).
# Обычные значения копируются и живут на стеке — память не волнует.
var a = 5
var b = a # b — независимая копия
b = 99
echo a # 5 (не изменилось)
# ref — управляемая ссылка (на куче), её освободит сборщик памяти
type
Node = ref object
value: int
next: Node # ссылка на такой же узел (рекурсивный тип)
var head = Node(value: 1)
head.next = Node(value: 2)
echo head.value # 1
echo head.next.value # 2
# new создаёт ref-объект вручную
var r: ref int
new(r) # выделить память
r[] = 42 # [] — разыменование (доступ к значению)
echo r[] # 42
# ptr — сырой небезопасный указатель (обычно только для C-интеропа)
var x = 10
let px = addr x # addr берёт адрес переменной
echo px[] # 10 — разыменование
px[] = 77 # запись по адресу
echo x # 77
12. Полезное: итераторы и перегрузка операторов
# iterator — ленивая последовательность для for. Возвращает значения через yield
iterator countTo(n: int): int =
var i = 1
while i <= n:
yield i # отдать очередное значение
inc i
for v in countTo(3):
echo v # 1 2 3
# Итератор по чётным числам
iterator evens(limit: int): int =
var i = 0
while i <= limit:
yield i
i += 2
for e in evens(8):
echo e # 0 2 4 6 8
# Перегрузка операторов: оператор — это обычная proc с символьным именем
type
Vec = object
x, y: int
proc `+`(a, b: Vec): Vec = # имя оператора в обратных кавычках
Vec(x: a.x + b.x, y: a.y + b.y)
let v = Vec(x: 1, y: 2) + Vec(x: 3, y: 4)
echo v.x, ", ", v.y # 4, 6
# Перегрузка $ задаёт строковое представление (как __str__ в Python)
proc `$`(v: Vec): string =
"Vec(" & $v.x & ", " & $v.y & ")"
echo Vec(x: 5, y: 9) # Vec(5, 9)
# На этом экспресс-тур по Nim завершён. Дальше: nimble (пакеты),
# async/await, макросы (macro) и компиляция в C/JS.