learnxinyminutes-docs/ru/crystal.md
2024-12-08 23:20:53 -07:00

582 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
filename: learncrystal-ru.cr
contributors:
- ["Vitalii Elenhaupt", "http://veelenga.com"]
- ["Arnaud Fernandés", "https://github.com/TechMagister/"]
translators:
- ["Den Patin", "https://github.com/denpatin"]
---
```crystal
# — так начинается комментарий
# Всё является объектом
nil.class #=> Nil
100.class #=> Int32
true.class #=> Bool
# Возвращают false только nil, false и пустые указатели
!nil #=> true : Bool
!false #=> true : Bool
!0 #=> false : Bool
# Целые числа
1.class #=> Int32
# Четыре типа целых чисел со знаком
1_i8.class #=> Int8
1_i16.class #=> Int16
1_i32.class #=> Int32
1_i64.class #=> Int64
# Четыре типа целых чисел без знака
1_u8.class #=> UInt8
1_u16.class #=> UInt16
1_u32.class #=> UInt32
1_u64.class #=> UInt64
2147483648.class #=> Int64
9223372036854775808.class #=> UInt64
# Двоичные числа
0b1101 #=> 13 : Int32
# Восьмеричные числа
0o123 #=> 83 : Int32
# Шестнадцатеричные числа
0xFE012D #=> 16646445 : Int32
0xfe012d #=> 16646445 : Int32
# Числа с плавающей точкой
1.0.class #=> Float64
# Два типа чисел с плавающей запятой
1.0_f32.class #=> Float32
1_f32.class #=> Float32
1e10.class #=> Float64
1.5e10.class #=> Float64
1.5e-7.class #=> Float64
# Символьные литералы
'a'.class #=> Char
# Восьмеричный код символа
'\101' #=> 'A' : Char
# Код символа Unicode
'\u0041' #=> 'A' : Char
# Строки
"s".class #=> String
# Строки неизменяемы
s = "hello, " #=> "hello, " : String
s.object_id #=> 134667712 : UInt64
s += "Crystal" #=> "hello, Crystal" : String
s.object_id #=> 142528472 : UInt64
# Поддерживается интерполяция строк
"sum = #{1 + 2}" #=> "sum = 3" : String
# Поддерживается многострочность
"This is
multiline string"
# Строка с двойными кавычками
%(hello "world") #=> "hello \"world\""
# Символы — константы без значения, определяемые только именем. Часто
# используются вместо часто используемых строк для лучшей производительности.
# На внутреннем уровне они представлены как Int32.
:symbol.class #=> Symbol
sentence = :question? # :"question?" : Symbol
sentence == :question? #=> true : Bool
sentence == :exclamation! #=> false : Bool
sentence == "question?" #=> false : Bool
# Массивы
[1, 2, 3].class #=> Array(Int32)
[1, "hello", 'x'].class #=> Array(Int32 | String | Char)
# При объявлении пустого массива необходимо указать тип его элементов
[] # Syntax error: for empty arrays use '[] of ElementType'
[] of Int32 #=> [] : Array(Int32)
Array(Int32).new #=> [] : Array(Int32)
# Элементы внутри массива имеют свои индексы
array = [1, 2, 3, 4, 5] #=> [1, 2, 3, 4, 5] : Array(Int32)
array[0] #=> 1 : Int32
array[10] # raises IndexError
array[-6] # raises IndexError
array[10]? #=> nil : (Int32 | Nil)
array[-6]? #=> nil : (Int32 | Nil)
# Можно получать элементы по индексу с конца
array[-1] #=> 5
# С начала и с указанием размера итогового массива
array[2, 3] #=> [3, 4, 5]
# Или посредством указания диапазона
array[1..3] #=> [2, 3, 4]
# Добавление в массив
array << 6 #=> [1, 2, 3, 4, 5, 6]
# Удаление элемента из конца массива
array.pop #=> 6
array #=> [1, 2, 3, 4, 5]
# Удаление элемента из начала массива
array.shift #=> 1
array #=> [2, 3, 4, 5]
# Проверка на наличие элемента в массиве
array.includes? 3 #=> true
# Синтаксический сахар для массива строк и символов
%w(one two three) #=> ["one", "two", "three"] : Array(String)
%i(one two three) #=> [:one, :two, :three] : Array(Symbol)
# Массивоподобный синтаксис используется и для других типов, только если для
# них определены методы .new и #<<
set = Set{1, 2, 3} #=> [1, 2, 3]
set.class #=> Set(Int32)
# Вышеприведенное эквивалентно следующему
set = Set(typeof(1, 2, 3)).new
set << 1
set << 2
set << 3
# Хэши
{1 => 2, 3 => 4}.class #=> Hash(Int32, Int32)
{1 => 2, 'a' => 3}.class #=> Hash(Int32 | Char, Int32)
# При объявлении пустого хэша необходимо указать типы ключа и значения
{} # Syntax error
{} of Int32 => Int32 # {}
Hash(Int32, Int32).new # {}
# Значения в хэше легко найти по ключу
hash = {"color" => "green", "number" => 5}
hash["color"] #=> "green"
hash["no_such_key"] #=> Missing hash key: "no_such_key" (KeyError)
hash["no_such_key"]? #=> nil
# Проверка наличия ключа в хэше
hash.has_key? "color" #=> true
# Синтаксический сахар для символьных и строковых ключей
{key1: 'a', key2: 'b'} # {:key1 => 'a', :key2 => 'b'}
{"key1": 'a', "key2": 'b'} # {"key1" => 'a', "key2" => 'b'}
# Хэшеподобный синтаксис используется и для других типов, только если для них
# определены методы .new и #[]=
class MyType
def []=(key, value)
puts "do stuff"
end
end
MyType{"foo" => "bar"}
# Вышеприведенное эквивалентно следующему
tmp = MyType.new
tmp["foo"] = "bar"
tmp
# Диапазоны
1..10 #=> Range(Int32, Int32)
Range.new(1, 10).class #=> Range(Int32, Int32)
# Включающий и исключающий диапазоны
(3..5).to_a #=> [3, 4, 5]
(3...5).to_a #=> [3, 4]
# Проверка на вхождение в диапазон
(1..8).includes? 2 #=> true
# Кортежи
# Неизменяемые последовательности фиксированного размера, содержащие,
# как правило, элементы разных типов
{1, "hello", 'x'}.class #=> Tuple(Int32, String, Char)
# Доступ к элементам осуществляется по индексу
tuple = {:key1, :key2}
tuple[1] #=> :key2
tuple[2] #=> syntax error : Index out of bound
# Элементы кортежей можно попарно присвоить переменным
a, b, c = {:a, 'b', "c"}
a #=> :a
b #=> 'b'
c #=> "c"
# Процедуры
# Указатели на функцию с необязательным содержимым (замыкание).
# Обычно создаётся с помощью специального литерала ->
proc = ->(x : Int32) { x.to_s }
proc.class # Proc(Int32, String)
# Или посредством метода .new
Proc(Int32, String).new { |x| x.to_s }
# Вызываются посредством метода .call
proc.call 10 #=> "10"
# Управляющие операторы
if true
"if statement"
elsif false
"else-if, optional"
else
"else, also optional"
end
puts "if as a suffix" if true
# if как часть выражения
a = if 2 > 1
3
else
4
end
a #=> 3
# Тернарный if
a = 1 > 2 ? 3 : 4 #=> 4
# Оператор выбора
cmd = "move"
action = case cmd
when "create"
"Creating..."
when "copy"
"Copying..."
when "move"
"Moving..."
when "delete"
"Deleting..."
end
action #=> "Moving..."
# Циклы
index = 0
while index <= 3
puts "Index: #{index}"
index += 1
end
# Index: 0
# Index: 1
# Index: 2
# Index: 3
index = 0
until index > 3
puts "Index: #{index}"
index += 1
end
# Index: 0
# Index: 1
# Index: 2
# Index: 3
# Но лучше использовать each
(1..3).each do |index|
puts "Index: #{index}"
end
# Index: 1
# Index: 2
# Index: 3
# Тип переменной зависит от типа выражения
if a < 3
a = "hello"
else
a = true
end
typeof a #=> (Bool | String)
if a && b
# здесь гарантируется, что и a, и b — не nil
end
if a.is_a? String
a.class #=> String
end
# Методы
def double(x)
x * 2
end
# Методы (а также любые блоки) всегда возвращают значение последнего выражения
double(2) #=> 4
# Скобки можно опускать, если вызов метода не вносит двусмысленности
double 3 #=> 6
double double 3 #=> 12
def sum(x, y)
x + y
end
# Параметры методов перечисляются через запятую
sum 3, 4 #=> 7
sum sum(3, 4), 5 #=> 12
# yield
# У всех методов есть неявный необязательный параметр блока, который можно
# вызвать ключевым словом yield
def surround
puts '{'
yield
puts '}'
end
surround { puts "hello world" }
# {
# hello world
# }
# Методу можно передать блок
# & — ссылка на переданный блок
def guests(&block)
block.call "some_argument"
end
# Методу можно передать список параметров, доступ к ним будет как к массиву
# Для этого используется оператор *
def guests(*array)
array.each { |guest| puts guest }
end
# Если метод возвращает массив, можно попарно присвоить значение каждого из его
# элементов переменным
def foods
["pancake", "sandwich", "quesadilla"]
end
breakfast, lunch, dinner = foods
breakfast #=> "pancake"
dinner #=> "quesadilla"
# По соглашению название методов, возвращающих булево значение, должно
# оканчиваться вопросительным знаком
5.even? # false
5.odd? # true
# Если название метода оканчивается восклицательным знаком, по соглашению это
# означает, что метод делает что-то необратимое, например изменяет получателя.
# Некоторые методы имеют две версии: "опасную" версию с !, которая что-то
# меняет, и "безопасную", которая просто возвращает новое значение
company_name = "Dunder Mifflin"
company_name.gsub "Dunder", "Donald" #=> "Donald Mifflin"
company_name #=> "Dunder Mifflin"
company_name.gsub! "Dunder", "Donald"
company_name #=> "Donald Mifflin"
# Классы
# Определяются с помощью ключевого слова class
class Human
# Переменная класса является общей для всех экземпляров этого класса
@@species = "H. sapiens"
# Объявление типа переменной name экземпляра класса
@name : String
# Базовый конструктор
# Значением первого параметра инициализируем переменную @name.
# То же делаем и со вторым параметром — переменная @age. В случае, если мы
# не передаём второй параметр, для инициализации @age будет взято значение
# по умолчанию (в данном случае — 0)
def initialize(@name, @age = 0)
end
# Базовый метод установки значения переменной
def name=(name)
@name = name
end
# Базовый метод получения значения переменной
def name
@name
end
# Синтаксический сахар одновременно для двух методов выше
property :name
# А также по отдельности
getter :name
setter :name
# Метод класса определяется ключевым словом self, чтобы его можно было
# различить с методом экземпляра класса. Такой метод можно вызвать только
# на уровне класса, а не экземпляра.
def self.say(msg)
puts msg
end
def species
@@species
end
end
# Создание экземпляра класса
jim = Human.new("Jim Halpert")
dwight = Human.new("Dwight K. Schrute")
# Вызов методов экземпляра класса
jim.species #=> "H. sapiens"
jim.name #=> "Jim Halpert"
jim.name = "Jim Halpert II" #=> "Jim Halpert II"
jim.name #=> "Jim Halpert II"
dwight.species #=> "H. sapiens"
dwight.name #=> "Dwight K. Schrute"
# Вызов метода класса
Human.say("Hi") #=> выведет "Hi" и вернёт nil
# Переменные экземпляра класса (@) видно только в пределах экземпляра
class TestClass
@var = "I'm an instance var"
end
# Переменные класса (@) видны как в экземплярах класса, так и в самом классе
class TestClass
@@var = "I'm a class var"
end
# Переменные с большой буквы — это константы
Var = "I'm a constant"
Var = "can't be updated" # Error: already initialized constant Var
# Примеры
# Базовый класс
class Human
@@foo = 0
def self.foo
@@foo
end
def self.foo=(value)
@@foo = value
end
end
# Класс-потомок
class Worker < Human
end
Human.foo #=> 0
Worker.foo #=> 0
Human.foo = 2 #=> 2
Worker.foo #=> 0
Worker.foo = 3 #=> 3
Human.foo #=> 2
Worker.foo #=> 3
module ModuleExample
def foo
"foo"
end
end
# Подключение модуля в класс добавляет его методы в экземпляр класса
# Расширение модуля добавляет его методы в сам класс
class Person
include ModuleExample
end
class Book
extend ModuleExample
end
Person.foo # => undefined method 'foo' for Person:Class
Person.new.foo # => 'foo'
Book.foo # => 'foo'
Book.new.foo # => undefined method 'foo' for Book
# Обработка исключений
# Создание пользовательского типа исключения
class MyException < Exception
end
# Ещё одного
class MyAnotherException < Exception; end
ex = begin
raise MyException.new
rescue ex1 : IndexError
"ex1"
rescue ex2 : MyException | MyAnotherException
"ex2"
rescue ex3 : Exception
"ex3"
rescue ex4 # без указания конкретного типа исключения будут "отлавливаться" все
"ex4"
end
ex #=> "ex2"
```
## Дополнительная информация
### На русском
- [Официальная документация](http://ru.crystal-lang.org/docs/)
### На английском
- [Official Documentation](http://crystal-lang.org/)