├── CNAME ├── _config.yml └── README.md /CNAME: -------------------------------------------------------------------------------- 1 | nim-lang.ru -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Учебное пособие по языку программирования Nim [альфа версия] 2 | 3 | - [Официальный сайт Nim](https://nim-lang.org/ "Язык программирования Nim.") 4 | - [Репозиторий на GitHub](https://github.com/nim-lang/Nim "GitHub") 5 | 6 | >Код в текущем пособии был проверен на Nim 0.18.0 7 | 8 | >26 сентября 2018 года вышла новая версия Nim 0.19.0. По этому мануал будет обновлен. [30.09.2018] 9 | 10 | ### Для кого создано это пособие 11 | Данное пособие предназначено для опытных программистов. 12 | Люди, которые только встали на этот тернистый путь, 13 | могут не понять некоторые аспекты, которые не будут раскрываться в тексте. 14 | 15 | ### Устновка компилятора 16 | Для Nim есть удобная утилита [choosenim](https://github.com/dom96/choosenim) (для Windows/Linux/macOS), с помощью которой можно легко устанавливать или обновлять Nim. 17 | 18 | Для Linux/macOS достаточно выполнить эту команду (убедитесь, что у вас есть curl и gcc): 19 | ```bash 20 | curl https://nim-lang.org/choosenim/init.sh -sSf | sh 21 | ``` 22 | 23 | Теперь добавьте к переменной PATH значение `~/.nimble/bin` в файл запуска вашего шелла (`~/.bashrc` для Bash, `~/.zshrc` для Zsh). 24 | 25 | Чтобы убедиться, что вы успешно установили `choosenim` и `nim`, выполните и проверьте, что обе команды вывели свои версии. 26 | ```bash 27 | choosenim -v && nim -v 28 | ``` 29 | 30 | ### Комментарии 31 | 32 | Однострочный комментарий. 33 | ```nim 34 | # какой-то комментарий 35 | ``` 36 | Многострочный комментарий. 37 | ```nim 38 | #[ 39 | комментарий 40 | на несколько 41 | строк 42 | ]# 43 | ``` 44 | 45 | Вложенные мнострочные комментарии. 46 | ```nim 47 | #[ 48 | вложенный 49 | #[комментарий]# 50 | #[на #[несколько]# ]# 51 | строк 52 | ]# 53 | ``` 54 | 55 | 56 | 57 | ### Типы данных 58 | 59 | `char` - ASCII символ 60 | ```nim 61 | var ch: char = 'a' 62 | var tab: char = '\t' 63 | ``` 64 | 65 | `string` - строка, выделяется двойными или тройными кавычками 66 | ```nim 67 | var login: string = "wtf" 68 | var mydata: string = """Эта строка - 69 | многострочная, так как мы использовали тройные кавычки 70 | """ 71 | ``` 72 | 73 | `int` - целое число. Для удобства записи больших чисел можно использовать символ `_` (он игнорируются) 74 | ```nim 75 | var age: int = 45 76 | var mysalary = 1_000_000 77 | ``` 78 | 79 | `uint` - беззнаковое целое. 80 | ```nim 81 | var bigNumber: uint = 1999999999999999 82 | ``` 83 | 84 | `float` - число с плавающей запятой 85 | ```nim 86 | var price: float = 45.45 87 | ``` 88 | 89 | `bool` - булев тип, может принимать значения true/false 90 | ```nim 91 | var good: bool = true 92 | var bad: bool = false 93 | ``` 94 | 95 | По умолчанию `int` и `float` являются платформозависимыми типами - если вы скомпилируете 32-битную программу, то int, uint и float будут равны типам `int32`, `uint32` и `float32`, 64-битную - `int64`, `uint64` и `float64` соответственно. Если вам необходимо явно указывать размер типов - используйте эти типы. Кроме этих размеров так же доступны `int8`, `uint8`, `int16`, `uint16` 96 | 97 | ### Переменные 98 | В Nim используется статическая типизация переменных, поэтому у переменной должен быть только один определённый тип, известный во время компиляции. 99 | 100 | Существует несколько типов переменных: 101 | 102 | `var` - переменная, которую можно изменять во время выполнения программы. Рекомендуется называть такие переменные в `camelCase` стиле: 103 | ```nim 104 | var myLogin: string = "valgala" 105 | var myPassword = "mypass" # Компилятор может сам выводить типы переменных 106 | myLogin = "valgala2" # Изменили переменную myLogin 107 | ``` 108 | 109 | `const` - константа. Её значение должно быть известно во время компиляции. 110 | Изменять её нельзя. Константам рекомендуется давать имя в `camelCase` или `PascalCase` стиле: 111 | ```nim 112 | const myOs = hostOS 113 | const MyData = 15 114 | ``` 115 | 116 | `let` - переменная с одиночным присваиванием. После присваивания её нельзя изменять. Её рекомендуется называть в том же стиле, что и `var` - `camelCase` 117 | ```nim 118 | let myint = 5 119 | ``` 120 | 121 | Для переменных можно использовать секции `var`/`let`/`const`. Эти секции должны быть выделены отступами (так как Nim основан на отступах). 122 | ```nim 123 | var 124 | name = "vera" 125 | age = 20 126 | 127 | const 128 | count = 15 129 | info = "Something" 130 | ``` 131 | 132 | ### Дополнительные типы 133 | В Nim можно объявлять новые типы. Делается это с помощью секции `type`. Рекомендуется называть типы в стиле `PascalCase`. 134 | 135 | `range` позволяет определить диапазон значений, среди которых могут быть целочисленные значения, `char` или перечисления (`enum`). 136 | ```nim 137 | type 138 | SubRange = range[1..12] 139 | 140 | var t: SubRange = 1 # входит в диапазон 141 | t = 12 # входит в диапазон 142 | t = 15 # Не входит в диапазон, будет ошибка во время компиляции 143 | ``` 144 | 145 | `enum` позволяет создавать новый тип *перечисления*. Значения у этого типа могут быть только из тех, которые были указаны при создании типа. 146 | ```nim 147 | type 148 | # Создаём наш тип EnumType, который представляет из себя enum с 149 | # возможными значениями one, two и three 150 | EnumType = enum 151 | one, two, three 152 | 153 | var a: EnumType = three 154 | var b = two 155 | echo a 156 | echo b 157 | ``` 158 | 159 | Любые значения `enum` можно конвертировать в `int`, так как Nim 160 | автоматически присваивает значениям числа по порядку, начиная с 0: 161 | ```nim 162 | type 163 | EnumType = enum 164 | one, two, three 165 | 166 | var a: enumType = three 167 | var b = two 168 | echo ord(a) # 2 169 | echo ord(b) # 1 170 | ``` 171 | 172 | Можно использовать свои числовые значения для перечислений, но они должны идти от меньшего к большему: 173 | ```nim 174 | type 175 | EnumType = enum 176 | one = 5, two = 8 177 | 178 | var a = one 179 | echo ord(a) # 5 180 | ``` 181 | 182 | К тому же перечислениям можно присваивать строковые значения: 183 | ```nim 184 | type 185 | EnumType = enum 186 | one = "Какая-то строка", two = 8 187 | 188 | var a = one 189 | echo ord(a) # индекс 190 | echo a # строка 191 | ``` 192 | 193 | Или задать сразу число и строку. 194 | ```nim 195 | type 196 | EnumType = enum 197 | one = (2, "Какая-то строка"), two = 8 198 | 199 | var a = one 200 | echo ord(a) # 2 201 | echo a # Какая-то строка 202 | ``` 203 | 204 | `array` - это массив фиксированного размера и одного типа. Размер массива должен быть известен во время компиляции. 205 | ```nim 206 | type 207 | ArrStr = array[0..2, string] # тип arrStr будет массивом из трех строк 208 | ArrStr2 = array[3, string] # получим тот же результат 209 | 210 | var myArr: ArrStr = ["asd", "sdf", "sdfg"] 211 | var myArr2: ArrStr2 = ["asd", "sdf", "sdfg", "123"] # Ошибка 212 | 213 | ``` 214 | 215 | Массивы можно создавать без указания размера и типа элементов: 216 | ```nim 217 | var arr = [1, 2, 3] 218 | ``` 219 | 220 | Получить доступ к определённому элементу можно по его индексу с помощью оператора `[]`: 221 | ```nim 222 | var arr = [1, 2, 3] 223 | echo arr[1] 224 | arr[1] = 5 225 | echo arr[1] 226 | 227 | # В данном массиве индекс начинается с 1 (т.к 1..3, а не 0..2) 228 | var anotherArr: array[1..3, string] = ["one", "two", "three"] 229 | echo anotherArr[1] # one 230 | ``` 231 | 232 | `seq` - это последовательность. Она похожа на массив, но отличается тем, что её размер можно изменять во время выполнения программы. 233 | Обьявлять последовательности можно с помошью указания `seq` и типа элементов самой последовательности. Последовательность можно создавать с помощю конструкции `@[ ]`. 234 | ```nim 235 | type 236 | MyStrSeq = seq[string] # последовательность из строк 237 | MyIntSeq = seq[int] # последовательность из чисел 238 | 239 | # Создали переменную с типом MyIntSeq, но не присвоили ей значение 240 | var mySeq: MyIntSeq 241 | 242 | mySeq = @[] # Присвоили переменной пустую последовательность 243 | mySeq = @[1, 2, 3] # Присвоили последовательность из чисел 1, 2 и 3 244 | 245 | # Можно использовать процедуру newSeq для создания последовательности 246 | var anotherSeq = newSeq[int]() 247 | 248 | # Компилятор может выводить тип последовательности автоматически 249 | let data = @["my", "data"] 250 | ``` 251 | 252 | `tuple` - кортеж. В кортеже может быть любое количество полей любого типа, но их количество нельзя изменять. Элементы в кортеже можно получать с помощью оператора индексирования `[]`. 253 | С помощью секции `type` или с помощью синтаксиса (ключ: значение) можно создавать именнованые кортежи, к элементам которых можно обращаться по названию поля через `.` 254 | ```nim 255 | type 256 | # Именованный кортеж 257 | Human = tuple[name: string, age: int] 258 | 259 | var 260 | me: Human = ("Dima", 30) 261 | friend: Human 262 | girl: Human 263 | 264 | # Обращение по индексу 265 | friend[0] = "Vova" 266 | friend[1] = 35 267 | 268 | # Обращение через . 269 | girl.name = "Ira" 270 | girl.age = 25 271 | 272 | # Просто кортеж 273 | let myTuple = (1, 2, "hello", 5.5) 274 | 275 | echo me, friend, girl, myTuple 276 | ``` 277 | 278 | ### Ветвления кода 279 | Ветвления создаются секциями `if`, `elif`, `else`. 280 | 281 | Если условие `if` секции истино (равняется `true`), то выполнится код внутри данной секции. 282 | ```nim 283 | if 2 == 2: 284 | echo "Оказывается, 2 равно 2!" 285 | ``` 286 | 287 | Если условие - ложь (`false`), то выполнится код внутри секции `else` (если она есть). 288 | ```nim 289 | if false: 290 | echo "Эта строка никогда не будет выведена" 291 | else: 292 | echo "А вот эта будет" 293 | ``` 294 | Если нужна дополнительная секция для другого условия, используется `elif`. 295 | ```nim 296 | if false: 297 | echo "Эта строка никогда не будет выведена" 298 | elif true: 299 | echo "А вот эта будет" 300 | else: 301 | echo "Эта секция кода не отработает, так как выполнился код в секции elif" 302 | ``` 303 | Еще один способ создавать ветвления на основе значений переменных - конструкция `case`. Выполнится только то ветвление, в значения которого входит переменная, по которой происходит ветвление (в данном случае - `myNumber`). 304 | 305 | Если ни одно ветвление не подходит, то выполняется блок `else` - он обязательный только тогда, когда у типа может быть любое значение. Блок `else` не нужен, если ветвление проводится по переменной, в тип которой входит ограниченное кол-во значений (к примеру типы `range`, `enum`), и все эти значения обработаны ветвлениями) 306 | ```nim 307 | let myNumber = 2 308 | 309 | case myNumber 310 | of 1: 311 | echo "Нам попался 1!" 312 | of 2, 3, 4: 313 | echo "Это либо 2, либо 3, либо 4" 314 | else: 315 | echo "Ни одно ветвление не подошло" 316 | 317 | let myData: range[0..5] = 3 318 | case myData 319 | # В case можно создавать ветвление по диапазону значений 320 | # Это работает для чисел, enum, char 321 | of 0..3: echo "Число от 0 до 3" 322 | of 4..5: echo "4 или 5" 323 | 324 | # Данная конструкция case не скомпилируется, так как обработаны не все 325 | # значения переменной myData 326 | case myData 327 | of 5: echo "Это 5!" 328 | ``` 329 | 330 | ### Циклы 331 | В Nim существует несколько типов циклов. 332 | 333 | `for` - проход по значения какого-либо итератора: 334 | ```nim 335 | for i in 20..25: 336 | echo i 337 | ``` 338 | 339 | - `..` - это итератор, который возвращает значения от начального числа до конечного (включительно). Он является сокращением к итератору `countup`, так как такие циклы встречаются в программах очень часто. 340 | - `for` - ключевое слово, которое объявляет цикл. 341 | - `i` - переменная, куда будет попадать текущее значение итератора. 342 | - `in` - указывает, по какому итератора проходит цикл. 343 | 344 | Ключево слово `break` прерывает цикл. 345 | ```nim 346 | for i in 20..30: 347 | if i == 25: 348 | break 349 | echo i 350 | ``` 351 | 352 | Ключевое слово `continue` завершает текущую итерацию. 353 | ```nim 354 | for i in countup(20,30): 355 | if i > 25: 356 | continue 357 | echo i 358 | ``` 359 | 360 | Пример цикла `while` 361 | ```nim 362 | var counter = 1 363 | 364 | while counter < 5: 365 | echo counter 366 | inc counter 367 | ``` 368 | 369 | - `while` выполняет тело цикла пока условие не станет равно `false`. 370 | - `inc` - добавляет 1 к переменной counter. 371 | 372 | ### Процедуры 373 | Процедуры обозначаются ключевым словом `proc`. Рекомендовано называть их в `camelCase` стиле. 374 | 375 | Их можно вызывать с помощью `()`: 376 | ```nim 377 | proc printSomething = 378 | echo "la la la" 379 | 380 | printSomething() 381 | ``` 382 | 383 | Указать *аргументы*, которые принимает процедура, можно, добавив их в скобки после названия процедуры. Нужно указывать название аргумента и его тип. 384 | Такие аргументы - обязательны, без их указания процедуру нельзя будет вызвать. 385 | ```nim 386 | proc printSomething(myString: string) = 387 | echo myString 388 | 389 | printSomething("Nim - классный язык программирования!") 390 | ``` 391 | 392 | Аргументы *по умолчанию* добавляются после знака `=`. 393 | Теперь, если данный аргумент не был передан в процедуру, будет использоваться значение по умолчанию. 394 | ```nim 395 | # При использовании значения по умолчанию тип указывать не обязательно 396 | proc printSomething(myString = "Математика - супер наука!") = 397 | echo myString 398 | 399 | printSomething() 400 | ``` 401 | 402 | Процедура может принимать *несколько аргументов*, тогда их нужно указывать через ``,`` . 403 | ```nim 404 | # myString - обязательный аргумент, needMath - нет (если его не указать), 405 | # то будет использоваться стандартное значение 406 | proc printSomething(myString: string, needMath = "Все знают, что да!") = 407 | echo myString, needMath 408 | 409 | printSomething("Математика нужна?") 410 | ``` 411 | 412 | Пример без значений по умолчанию. 413 | ```nim 414 | proc printMe(name: string, year: int) = 415 | echo "Меня зовут " & name & "." 416 | echo "Я живу в " & $year & " году." 417 | 418 | printMe("Фрай", 3000) 419 | ``` 420 | 421 | - `&` - конкатенация (объединение) строк. 422 | - `$` - оператор преобразования числа в строку. 423 | 424 | Можно указать тип сразу для нескольких параметров. 425 | ```nim 426 | proc point(x, y, z: int) = 427 | echo "Координаты в пространстве:" 428 | echo "x=" & $x 429 | echo "y=" & $y 430 | echo "z=" & $z 431 | 432 | point(10, 11, 12) 433 | ``` 434 | 435 | Выражение `return` позволяет возвращать что-либо из функции. 436 | Тип возвращаемого значения процедуры обязательно должен быть указан после скобок с аргументами (если они есть), или после названия процедуры 437 | ```nim 438 | proc getString: string = 439 | return "Свободу попугаям!" 440 | 441 | echo getString() 442 | 443 | proc returnSame(data: int): int = 444 | return data 445 | 446 | echo returnSame(5) # Выведет 5 447 | ``` 448 | 449 | Кроме того, во всех процедурах, которые что-то возвращают, доступна переменная 450 | *result*. Её можно использовать для того, чтобы указать возвращаемое значение, но не выходить из процедуры (после выполнения кода процедуры переменная *result* автоматически возвратится): 451 | ```nim 452 | proc someMath(data: int): int = 453 | # Приравниваем результат к аргументу data 454 | result = data 455 | # Умножаем результат на 5 456 | result = result * 5 457 | if result > 10: 458 | result = result + 2 459 | # Прибавляем 3 460 | result = result + 3 461 | 462 | echo someMath(5) # Выведет 30 463 | echo someMath(2) # Выведет 13 464 | ``` 465 | 466 | Можно использовать перегрузку функций (несколько процедур с одинаковым названием, но разными аргументами) 467 | ```nim 468 | proc overloadMe(x: int, y: int)= 469 | echo x, y 470 | 471 | proc overloadMe(x: int)= 472 | echo x 473 | 474 | overloadMe(1) 475 | overloadMe(1, 2) 476 | ``` 477 | 478 | Если у процедуры не указывать название, она будет анонимной: 479 | ```nim 480 | var anonymous = proc(x: string) = 481 | echo x 482 | 483 | anonymous("Я - анонимная процедура :)") 484 | ``` 485 | 486 | Пример *передачи* процедуры в качестве аргумента. 487 | ```nim 488 | proc f(): int = 489 | return 5 490 | 491 | proc anotherF(data: float): string = 492 | # $ конвертирует float в строку 493 | return $data 494 | 495 | # Передача процедуры без аргументов 496 | proc wrap(fun: proc)= 497 | echo fun() 498 | 499 | proc wrap(fun: proc(data: float): string, info: float) = 500 | # Вызываем процедуру fun с аргументом, который нам передали в info 501 | echo fun(info) 502 | 503 | wrap(f) 504 | wrap(anotherF, 5.0) 505 | ``` 506 | 507 | Пример *возврата* процедуры. 508 | ```nim 509 | proc get(): proc = 510 | # В коротких процедурах не обязательно писать "result" или "return" 511 | # Автоматически возвратится выражение, которое указано в теле процедуры 512 | proc res(): int = 5 513 | return res 514 | 515 | echo get()() # 5 516 | ``` 517 | 518 | Если мы хотим иметь возможность передавать в одну и ту же процедуру и массив, и последовательность, то можно использовать openArray. 519 | 520 | Этот тип данных может использоваться только в качестве аргумента процедуры. 521 | Для openArray нужно обязательно указать тип элементов 522 | ```nim 523 | # a будет содержать какое-то количество элементов с типом int 524 | proc echoContainer(a: openArray[int])= 525 | for i in a: 526 | echo i 527 | 528 | echoContainer(@[1,2,3,4,5]) # Последовательность 529 | echoContainer([1,2,3,4,5]) # Массив 530 | ``` 531 | 532 | Если в процедуру необходимо передавать произвольное число аргументов, то можно 533 | использовать varargs 534 | ```nim 535 | # varargs будет содержать элементы типа string 536 | proc printEverything(a: varargs[string])= 537 | for i in a: 538 | echo i 539 | 540 | printEverything("as", "sd", "df") 541 | ``` 542 | 543 | varargs позволяет применять ко всем аргументам какую-либо процедуру для того, чтобы привести аргументы разного типа к одному. 544 | ```nim 545 | # varargs будет содержать только аргументы типа string, так как все 546 | # другие типы будут сконвертированы в строку при помощи процедуры $ 547 | proc convVarargs(a: varargs[string, `$`])= 548 | for i in a: 549 | echo i 550 | 551 | convVarargs("as", "sd", 21, 5.0, false) 552 | ``` 553 | 554 | ### Итераторы 555 | Если в процедуре заменить `proc` на `iterator` а `return` на `yield`, то получится итератор. 556 | ```nim 557 | iterator printItem(): int = 558 | for i in 1..5: 559 | yield i 560 | 561 | for i in printItem(): 562 | echo i 563 | ``` 564 | 565 | ### Преобразование типов 566 | 567 | [comment]: # (#TODO: сделать эту секцию) 568 | ### Исключения 569 | В Nim исключения обрабатываются с помощью секций `try` и `except` (так же, как и в Python). В секцию `try` мы помещаем код, который может вызвать исключение, которое нужно будет обработать. Если происходит исключение, то начинается выполнение кода в секции `except`. В `finally` код выполняется в любом случае. 570 | ```nim 571 | try: 572 | var arr = @[1,2,3] 573 | echo arr[5] 574 | except: 575 | echo "У последовательности arr такого индекса нет!" 576 | ``` 577 | 578 | [comment]: # (#TODO: написать о "except Exception as exc" и выводе текста самого исключения) 579 | 580 | После `except` мы можем указать, какой тип исключения мы отлавливаем (аналогично Python). 581 | ```nim 582 | try: 583 | var arr = @[1,2,3] 584 | echo arr[5] 585 | # Отлавливаем только обращения к несуществующему индексу 586 | # Другие ошибки в данном случае не будут пойманы 587 | except IndexError: 588 | echo "Элемента по такому индексу не существует" 589 | ``` 590 | 591 | Блок `finally` выполняется после `try` и (или) `except` 592 | ```nim 593 | try: 594 | var arr = @[1,2,3] 595 | echo arr[5] 596 | except IOError: 597 | echo "Элемента по такому индексу не существует" 598 | finally: 599 | echo "Я всегда выполняюсь" 600 | ``` 601 | 602 | ### Шаблоны 603 | 604 | [comment]: # (#TODO: сделать эту секцию) 605 | ### Макросы 606 | 607 | [comment]: # (#TODO: сделать эту секцию) 608 | ### Модули 609 | Nim позволяет разбивать код на модули. Один файл это один модуль. 610 | Для того, чтобы процедуру/переменную/тип видели другие модули, её нужно пометить с помощью экспортирующего маркера (`*`) 611 | ```nim 612 | # Файл one.nim 613 | var name* = "Vova" # Доступна из других модулей 614 | var lastname = "Sidorov" # Недоступна 615 | 616 | proc exportedProc* = # Можно вызывать из других модулей 617 | echo "exportme" 618 | 619 | proc notExportedProc = # Нельзя 620 | echo "notexportme" 621 | ``` 622 | 623 | Теперь в другом файле вы можете сделать `import one` и пользоваться импортированными переменными, процедурами, типами. 624 | ```nim 625 | # Файл two.nim 626 | import one 627 | exportedProc() # Выведет "exportme" 628 | notExportedProc() # Ошибка 629 | ``` 630 | 631 | Если мы не хотим что-либо импортировать из другого модуля, мы можем использовать `except`. 632 | ```nim 633 | # Файл two.nim 634 | import one except name # Переменная name не будет импортирована 635 | exportedProc() # Выведет "exportme" 636 | echo name # Ошибка - мы не импортировали name 637 | ``` 638 | 639 | Так же мы можем импортировать только указанные символы с помощью `from`. 640 | ```nim 641 | # Файл two.nim 642 | from one import exportedProc 643 | exportedProc() 644 | echo name # Ошибка - мы импортировали только exportedProc 645 | ``` 646 | 647 | При импорте модуля с помощью конструкции ``from module import nil`` доступ к любым символам этого модуля должен быть с указанием названия этого модуля (так же, как в Python при ``import module``) 648 | ```nim 649 | # Файл two.nim 650 | from one import nil 651 | one.exportedProc() # Сработает 652 | exportedProc() # Ошибка 653 | ``` 654 | 655 | Можно использовать свои названия для импортированных модулей через `as`. 656 | ```nim 657 | # Файл two.nim 658 | # Импортируем модуль one с названием "x" и разрешаем доступ к символам 659 | # только при указании этого имени 660 | from one as x import nil 661 | x.exportedProc() 662 | ``` 663 | 664 | Команда `include` позволяет вставлять содержимое файла в своё местоположение. 665 | Её рекомендуется использовать только тогда, когда большой файл нельзя разделить на модули - тогда можно его разделить с помощью include. 666 | ```nim 667 | include one 668 | exportedProc() 669 | ``` 670 | 671 | ### Прагмы 672 | Если нам нужно сообщить компилятору какую-либо информацию, то можно воспользоваться прагмами. 673 | Они указываются с помощью фигурных скобок и названия прагмы - `{.somepragma.}` 674 | ```nim 675 | # При компиляции - Hint: 'name' is declared but not used [XDeclaredButNotUsed] 676 | var name {.deprecated.} : string 677 | ``` 678 | 679 | Прагма `{.deprecated.}` сообщает компилятору, что переменная `name` устарела, и будет выведено предупреждение во время компиляции. 680 | 681 | Есть прагма `{.hint.}`, которая позволяет выводить какое-то сообщение при компиляции. 682 | ```nim 683 | # Во время компиляции выведется сообщение - Hint: какая-то строка [User] 684 | {.hint: "какая-то строка".} 685 | ``` 686 | 687 | ### ООП 688 | Для создания обьектного типа необходимо использовать `object` в блоке `type`. 689 | Обьекты такого типа попадают в стек. Новые объекты можно создавать несколькими способами. 690 | ```nim 691 | type 692 | Person = object 693 | name: string 694 | age: int 695 | 696 | # 1-й способ - "вызов" типа 697 | var noOne = Person() # Значения полей будут стандартными 698 | 699 | var me = Person(name: "Кирилл", age: 27) 700 | 701 | # 2-й способ - создание переменной Person и назначение полей 702 | var friend: Person 703 | friend.name = "Вадим" 704 | friend.age = 23 705 | 706 | echo "Я - " & $me 707 | echo "Друг - " & $friend 708 | ``` 709 | 710 | Объекты, объявленные с `ref` будут контролироваться сборщиком мусора, и они будут передаваться по ссылке, а не по значению (как обычные объекты). 711 | ```nim 712 | type 713 | Person = ref object 714 | name: string 715 | age: int 716 | 717 | var me = Person(name: "Ира", age: 30) 718 | echo me.name 719 | ``` 720 | 721 | Наследование можно осуществлять с помощью ключевого слова `of`. 722 | Нельзя наследоваться от финального обьекта. Для этого объект, от которого будут наследоваться другие, сам должен наследоваться от `RootObj`. 723 | ```nim 724 | type 725 | Person = ref object of RootObj 726 | name: string 727 | age: int 728 | Student = ref object of Person # Наследуемся от Person 729 | sex: string 730 | 731 | var me = Student(name: "Иван", age: 15, sex: "мужской") 732 | echo me.sex 733 | ``` 734 | 735 | Nim поддерживает *UFCS* (Unified Function Call Syntax), поэтому в данном примере 736 | ``getObj(me)`` означает то же самое, что и ``me.getObj()`` 737 | ```nim 738 | type 739 | Person = object 740 | 741 | proc getObj(this: Person): string = 742 | return "Yes, this is Person" 743 | 744 | var me = Person() 745 | # Оба echo выведут одно и то же 746 | echo me.getObj() 747 | echo getObj(me) 748 | ``` 749 | 750 | Если нужны мультиметоды, то вместо `proc` необходимо использовать `method` и прагму `{.base.}` для базового метода. 751 | ```nim 752 | type 753 | Person = object 754 | 755 | # Базовый метод 756 | method getObj(this: Person): string {.base.} = 757 | return "Person" 758 | 759 | var me = Person() # создаем объект 760 | echo me.getObj() # вызываем метод 761 | ``` 762 | 763 | В процедурах используется статическое связывание, оно происходит во время компиляции. 764 | В методах (`method`) используется динамическое связывание, оно происходит во время выполения программы. 765 | Именно динамическое связывание позволяет создавать мультиметоды для реализации наследования (как в традиционном ООП) 766 | ```nim 767 | type 768 | # Тип Person, от которого будем наследовать другие объекты 769 | Person = object of RootObj 770 | # Объекты Man и Woman наследованы от Person 771 | Man = object of Person 772 | Woman = object of Person 773 | 774 | proc getObj(this: Person): string = 775 | return "Person" 776 | 777 | proc getObj(this: Man): string = 778 | return "Man" 779 | 780 | # Статическая привязка (во время компиляции) 781 | proc print(this: Person) = 782 | # Мы точно знаем, что процедура получит на вход Person 783 | echo this.getObj() 784 | 785 | method getObj2(this: Person): string {.base.} = 786 | return "Person2" 787 | 788 | # А здесь привязка во время компиляции не происходит 789 | method print2(this: Person) {.base.} = 790 | # Во время выполения происходит подстановка объектного типа, 791 | # поэтому данный метод можно вызвать для любого типа, который наследуется 792 | # от Person (как и для самого Person) 793 | echo this.getObj2() 794 | 795 | method getObj2(this: Man): string = 796 | return "Man2" 797 | 798 | method getObj2(this: Woman): string = 799 | return "Woman" 800 | 801 | var me = Man() 802 | 803 | # Мы вызваем print, и ему чётко ясно, что он работаем с Person 804 | # И уже не важно, что объект типа Man, так как связывание уже прошло 805 | me.print() # Выведет Person 806 | 807 | # А print2 сработает хоть с Man, хоть с Woman, хоть с Person 808 | me.print2() # Выведет Men2 809 | 810 | var w = Woman() 811 | w.print2() # Выведет Woman 812 | ``` 813 | 814 | Обратиться к атрибуту внутри процедуры можно так: 815 | ```nim 816 | type 817 | Obj = object 818 | number: int 819 | 820 | # Вместо self можно использовать любое другое название аргумента 821 | proc get(self: Obj): int = 822 | return self.number # Возвращаем атрибут объекта "number" 823 | 824 | var o = Obj(number: 15) 825 | echo o.get() 826 | ``` 827 | 828 | Если объектам необходимо присваивать значения, то нужно добавлять `var` к их типу в аргуменах процедуры. При этом объект будет передаваться по ссылке (так же, как и `ref` объект по умолчанию) 829 | ```nim 830 | type 831 | Obj = object 832 | number: int 833 | 834 | proc get(self: Obj): int = 835 | return self.number 836 | 837 | # В self мы на самом деле получаем ссылку на объект, но Nim позволяет нам 838 | # "не знать" об этом (т.е не нужно никаких других добавлений) 839 | proc set(self: var Obj, x: int) = 840 | self.number = x 841 | 842 | var o = Obj(number: 15) 843 | o.set(45) 844 | echo o.get() # Выведет 45 845 | ``` 846 | 847 | ### Дженерики 848 | Дженерики - это процедуры, которые могут принимать разные типы в качестве аргументов и изменять своё поведение на основе них. 849 | 850 | Между `[]` после названия процедуры указываются типы дженерика. `[T]` - любой тип. `[T: int|float]` - только `int` или `float`. 851 | ```nim 852 | # echoSomething принимает аргумент x, который может быть любым типом 853 | proc echoSomething[T](x: T) = 854 | # Однако, если для какого-либо из типов нет процедуры $ для перевода в 855 | # строку, то будет ошибка во время компиляции 856 | echo x 857 | 858 | echoSomething(45) 859 | echoSomething(45.45) 860 | echoSomething("строка") 861 | 862 | # intOrFloat принимает аргумент x, который может быть типом int или float 863 | proc intOrFloat[T: int|float](x: T) = 864 | echo x 865 | 866 | intOrFloat(45) 867 | intOrFloat(45.45) 868 | intOrFloat("будет ошибка") # Ошибка, intOrFloat не принимает строки 869 | 870 | # lol принимает x типа int или float, и y типа char или string 871 | proc lol[T: int|float, D: char|string](x: T, y: D) = 872 | echo x 873 | echo y 874 | 875 | lol(45,'a') 876 | lol(45.45, "sdf") 877 | 878 | lol("sdf", 45) # Ошибка, x должен быть типом int или float 879 | ``` 880 | 881 | ### Потоки и параллелизм 882 | 883 | [comment]: # (#TODO: сделать эту секцию) 884 | ### Создание динамических библиотек 885 | 886 | [comment]: # (#TODO: сделать эту секцию) 887 | ### Компиляция 888 | Для компиляции файла используется команда: 889 | ```bash 890 | nim c -r file.nim 891 | ``` 892 | 893 | * `nim` - вызываем компилятор и передаем ему параметры 894 | * `c` - сокращение от `compile`, компилирует файл с помощью стандартного бекенда (Си) 895 | * `-r` - сокращение от `--run`. Скомпилированный бинарник сразу запустится 896 | * `file.nim` - файл с исходным кодом который мы хотим скомпилировать 897 | 898 | Для компиляции в релизном режиме (с максимальными оптимизациями) нужно добавить `-d:release` к команде. 899 | 900 | ### Ссылки 901 | - [https://nim-lang.org/docs/manual.html](https://nim-lang.org/docs/manual.html) 902 | - [https://nim-lang.org/docs/tut1.html](https://nim-lang.org/docs/tut1.html) 903 | - [https://nim-lang.org/docs/tut2.html](https://nim-lang.org/docs/tut2.html) 904 | - [https://nim-lang.org/docs/lib.html](https://nim-lang.org/docs/lib.html) 905 | - [https://nim-by-example.github.io](https://nim-by-example.github.io) 906 | - [http://rosettacode.org/wiki/Category:Nim](http://rosettacode.org/wiki/Category:Nim) 907 | - [http://devdocs.io/nim/](http://devdocs.io/nim/) 908 | - [https://play.nim-lang.org/](https://play.nim-lang.org/) 909 | - [https://github.com/dom96/choosenim](https://github.com/dom96/choosenim) 910 | - [https://nim-by-example.github.io/](https://nim-by-example.github.io/) 911 | - 912 | --------------------------------------------------------------------------------