1# — так начинается комментарий
2
3
4# Всё является объектом
5nil.class #=> Nil
6100.class #=> Int32
7true.class #=> Bool
8
9# Возвращают false только nil, false и пустые указатели
10!nil #=> true : Bool
11!false #=> true : Bool
12!0 #=> false : Bool
13
14
15# Целые числа
16
171.class #=> Int32
18
19# Четыре типа целых чисел со знаком
201_i8.class #=> Int8
211_i16.class #=> Int16
221_i32.class #=> Int32
231_i64.class #=> Int64
24
25# Четыре типа целых чисел без знака
261_u8.class #=> UInt8
271_u16.class #=> UInt16
281_u32.class #=> UInt32
291_u64.class #=> UInt64
30
312147483648.class #=> Int64
329223372036854775808.class #=> UInt64
33
34# Двоичные числа
350b1101 #=> 13 : Int32
36
37# Восьмеричные числа
380o123 #=> 83 : Int32
39
40# Шестнадцатеричные числа
410xFE012D #=> 16646445 : Int32
420xfe012d #=> 16646445 : Int32
43
44# Числа с плавающей точкой
45
461.0.class #=> Float64
47
48# Два типа чисел с плавающей запятой
491.0_f32.class #=> Float32
501_f32.class #=> Float32
51
521e10.class #=> Float64
531.5e10.class #=> Float64
541.5e-7.class #=> Float64
55
56
57# Символьные литералы
58
59'a'.class #=> Char
60
61# Восьмеричный код символа
62'\101' #=> 'A' : Char
63
64# Код символа Unicode
65'\u0041' #=> 'A' : Char
66
67
68# Строки
69
70"s".class #=> String
71
72# Строки неизменяемы
73s = "hello, " #=> "hello, " : String
74s.object_id #=> 134667712 : UInt64
75s += "Crystal" #=> "hello, Crystal" : String
76s.object_id #=> 142528472 : UInt64
77
78# Поддерживается интерполяция строк
79"sum = #{1 + 2}" #=> "sum = 3" : String
80
81# Поддерживается многострочность
82"This is
83 multiline string"
84
85# Строка с двойными кавычками
86%(hello "world") #=> "hello \"world\""
87
88
89# Символы — константы без значения, определяемые только именем. Часто
90# используются вместо часто используемых строк для лучшей производительности.
91# На внутреннем уровне они представлены как Int32.
92
93:symbol.class #=> Symbol
94
95sentence = :question? # :"question?" : Symbol
96
97sentence == :question? #=> true : Bool
98sentence == :exclamation! #=> false : Bool
99sentence == "question?" #=> false : Bool
100
101
102# Массивы
103
104[1, 2, 3].class #=> Array(Int32)
105[1, "hello", 'x'].class #=> Array(Int32 | String | Char)
106
107# При объявлении пустого массива необходимо указать тип его элементов
108[] # Syntax error: for empty arrays use '[] of ElementType'
109[] of Int32 #=> [] : Array(Int32)
110Array(Int32).new #=> [] : Array(Int32)
111
112# Элементы внутри массива имеют свои индексы
113array = [1, 2, 3, 4, 5] #=> [1, 2, 3, 4, 5] : Array(Int32)
114array[0] #=> 1 : Int32
115array[10] # raises IndexError
116array[-6] # raises IndexError
117array[10]? #=> nil : (Int32 | Nil)
118array[-6]? #=> nil : (Int32 | Nil)
119
120# Можно получать элементы по индексу с конца
121array[-1] #=> 5
122
123# С начала и с указанием размера итогового массива
124array[2, 3] #=> [3, 4, 5]
125
126# Или посредством указания диапазона
127array[1..3] #=> [2, 3, 4]
128
129# Добавление в массив
130array << 6 #=> [1, 2, 3, 4, 5, 6]
131
132# Удаление элемента из конца массива
133array.pop #=> 6
134array #=> [1, 2, 3, 4, 5]
135
136# Удаление элемента из начала массива
137array.shift #=> 1
138array #=> [2, 3, 4, 5]
139
140# Проверка на наличие элемента в массиве
141array.includes? 3 #=> true
142
143# Синтаксический сахар для массива строк и символов
144%w(one two three) #=> ["one", "two", "three"] : Array(String)
145%i(one two three) #=> [:one, :two, :three] : Array(Symbol)
146
147# Массивоподобный синтаксис используется и для других типов, только если для
148# них определены методы .new и #<<
149set = Set{1, 2, 3} #=> [1, 2, 3]
150set.class #=> Set(Int32)
151
152# Вышеприведенное эквивалентно следующему
153set = Set(typeof(1, 2, 3)).new
154set << 1
155set << 2
156set << 3
157
158
159# Хэши
160
161{1 => 2, 3 => 4}.class #=> Hash(Int32, Int32)
162{1 => 2, 'a' => 3}.class #=> Hash(Int32 | Char, Int32)
163
164# При объявлении пустого хэша необходимо указать типы ключа и значения
165{} # Syntax error
166{} of Int32 => Int32 # {}
167Hash(Int32, Int32).new # {}
168
169# Значения в хэше легко найти по ключу
170hash = {"color" => "green", "number" => 5}
171hash["color"] #=> "green"
172hash["no_such_key"] #=> Missing hash key: "no_such_key" (KeyError)
173hash["no_such_key"]? #=> nil
174
175# Проверка наличия ключа в хэше
176hash.has_key? "color" #=> true
177
178# Синтаксический сахар для символьных и строковых ключей
179{key1: 'a', key2: 'b'} # {:key1 => 'a', :key2 => 'b'}
180{"key1": 'a', "key2": 'b'} # {"key1" => 'a', "key2" => 'b'}
181
182# Хэшеподобный синтаксис используется и для других типов, только если для них
183# определены методы .new и #[]=
184class MyType
185 def []=(key, value)
186 puts "do stuff"
187 end
188end
189
190MyType{"foo" => "bar"}
191
192# Вышеприведенное эквивалентно следующему
193tmp = MyType.new
194tmp["foo"] = "bar"
195tmp
196
197
198# Диапазоны
199
2001..10 #=> Range(Int32, Int32)
201Range.new(1, 10).class #=> Range(Int32, Int32)
202
203# Включающий и исключающий диапазоны
204(3..5).to_a #=> [3, 4, 5]
205(3...5).to_a #=> [3, 4]
206
207# Проверка на вхождение в диапазон
208(1..8).includes? 2 #=> true
209
210
211# Кортежи
212# Неизменяемые последовательности фиксированного размера, содержащие,
213# как правило, элементы разных типов
214
215{1, "hello", 'x'}.class #=> Tuple(Int32, String, Char)
216
217# Доступ к элементам осуществляется по индексу
218tuple = {:key1, :key2}
219tuple[1] #=> :key2
220tuple[2] #=> syntax error : Index out of bound
221
222# Элементы кортежей можно попарно присвоить переменным
223a, b, c = {:a, 'b', "c"}
224a #=> :a
225b #=> 'b'
226c #=> "c"
227
228
229# Процедуры
230# Указатели на функцию с необязательным содержимым (замыкание).
231# Обычно создаётся с помощью специального литерала ->
232
233proc = ->(x : Int32) { x.to_s }
234proc.class # Proc(Int32, String)
235# Или посредством метода .new
236Proc(Int32, String).new { |x| x.to_s }
237
238# Вызываются посредством метода .call
239proc.call 10 #=> "10"
240
241
242# Управляющие операторы
243
244if true
245 "if statement"
246elsif false
247 "else-if, optional"
248else
249 "else, also optional"
250end
251
252puts "if as a suffix" if true
253
254# if как часть выражения
255a = if 2 > 1
256 3
257 else
258 4
259 end
260
261a #=> 3
262
263# Тернарный if
264a = 1 > 2 ? 3 : 4 #=> 4
265
266# Оператор выбора
267cmd = "move"
268
269action = case cmd
270 when "create"
271 "Creating..."
272 when "copy"
273 "Copying..."
274 when "move"
275 "Moving..."
276 when "delete"
277 "Deleting..."
278end
279
280action #=> "Moving..."
281
282
283# Циклы
284
285index = 0
286while index <= 3
287 puts "Index: #{index}"
288 index += 1
289end
290# Index: 0
291# Index: 1
292# Index: 2
293# Index: 3
294
295index = 0
296until index > 3
297 puts "Index: #{index}"
298 index += 1
299end
300# Index: 0
301# Index: 1
302# Index: 2
303# Index: 3
304
305# Но лучше использовать each
306(1..3).each do |index|
307 puts "Index: #{index}"
308end
309# Index: 1
310# Index: 2
311# Index: 3
312
313# Тип переменной зависит от типа выражения
314if a < 3
315 a = "hello"
316else
317 a = true
318end
319typeof a #=> (Bool | String)
320
321if a && b
322 # здесь гарантируется, что и a, и b — не nil
323end
324
325if a.is_a? String
326 a.class #=> String
327end
328
329
330# Методы
331
332def double(x)
333 x * 2
334end
335
336# Методы (а также любые блоки) всегда возвращают значение последнего выражения
337double(2) #=> 4
338
339# Скобки можно опускать, если вызов метода не вносит двусмысленности
340double 3 #=> 6
341
342double double 3 #=> 12
343
344def sum(x, y)
345 x + y
346end
347
348# Параметры методов перечисляются через запятую
349sum 3, 4 #=> 7
350
351sum sum(3, 4), 5 #=> 12
352
353
354# yield
355
356# У всех методов есть неявный необязательный параметр блока, который можно
357# вызвать ключевым словом yield
358
359def surround
360 puts '{'
361 yield
362 puts '}'
363end
364
365surround { puts "hello world" }
366
367# {
368# hello world
369# }
370
371# Методу можно передать блок
372# & — ссылка на переданный блок
373def guests(&block)
374 block.call "some_argument"
375end
376
377# Методу можно передать список параметров, доступ к ним будет как к массиву
378# Для этого используется оператор *
379def guests(*array)
380 array.each { |guest| puts guest }
381end
382
383# Если метод возвращает массив, можно попарно присвоить значение каждого из его
384# элементов переменным
385def foods
386 ["pancake", "sandwich", "quesadilla"]
387end
388breakfast, lunch, dinner = foods
389breakfast #=> "pancake"
390dinner #=> "quesadilla"
391
392# По соглашению название методов, возвращающих булево значение, должно
393# оканчиваться вопросительным знаком
3945.even? # false
3955.odd? # true
396
397# Если название метода оканчивается восклицательным знаком, по соглашению это
398# означает, что метод делает что-то необратимое, например изменяет получателя.
399# Некоторые методы имеют две версии: "опасную" версию с !, которая что-то
400# меняет, и "безопасную", которая просто возвращает новое значение
401company_name = "Dunder Mifflin"
402company_name.gsub "Dunder", "Donald" #=> "Donald Mifflin"
403company_name #=> "Dunder Mifflin"
404company_name.gsub! "Dunder", "Donald"
405company_name #=> "Donald Mifflin"
406
407
408# Классы
409# Определяются с помощью ключевого слова class
410
411class Human
412
413 # Переменная класса является общей для всех экземпляров этого класса
414 @@species = "H. sapiens"
415
416 # Объявление типа переменной name экземпляра класса
417 @name : String
418
419 # Базовый конструктор
420 # Значением первого параметра инициализируем переменную @name.
421 # То же делаем и со вторым параметром — переменная @age. В случае, если мы
422 # не передаём второй параметр, для инициализации @age будет взято значение
423 # по умолчанию (в данном случае — 0)
424 def initialize(@name, @age = 0)
425 end
426
427 # Базовый метод установки значения переменной
428 def name=(name)
429 @name = name
430 end
431
432 # Базовый метод получения значения переменной
433 def name
434 @name
435 end
436
437 # Синтаксический сахар одновременно для двух методов выше
438 property :name
439
440 # А также по отдельности
441 getter :name
442 setter :name
443
444 # Метод класса определяется ключевым словом self, чтобы его можно было
445 # различить с методом экземпляра класса. Такой метод можно вызвать только
446 # на уровне класса, а не экземпляра.
447 def self.say(msg)
448 puts msg
449 end
450
451 def species
452 @@species
453 end
454end
455
456
457# Создание экземпляра класса
458jim = Human.new("Jim Halpert")
459
460dwight = Human.new("Dwight K. Schrute")
461
462# Вызов методов экземпляра класса
463jim.species #=> "H. sapiens"
464jim.name #=> "Jim Halpert"
465jim.name = "Jim Halpert II" #=> "Jim Halpert II"
466jim.name #=> "Jim Halpert II"
467dwight.species #=> "H. sapiens"
468dwight.name #=> "Dwight K. Schrute"
469
470# Вызов метода класса
471Human.say("Hi") #=> выведет "Hi" и вернёт nil
472
473# Переменные экземпляра класса (@) видно только в пределах экземпляра
474class TestClass
475 @var = "I'm an instance var"
476end
477
478# Переменные класса (@) видны как в экземплярах класса, так и в самом классе
479class TestClass
480 @@var = "I'm a class var"
481end
482
483# Переменные с большой буквы — это константы
484Var = "I'm a constant"
485Var = "can't be updated" # Error: already initialized constant Var
486
487# Примеры
488
489# Базовый класс
490class Human
491 @@foo = 0
492
493 def self.foo
494 @@foo
495 end
496
497 def self.foo=(value)
498 @@foo = value
499 end
500end
501
502# Класс-потомок
503class Worker < Human
504end
505
506Human.foo #=> 0
507Worker.foo #=> 0
508
509Human.foo = 2 #=> 2
510Worker.foo #=> 0
511
512Worker.foo = 3 #=> 3
513Human.foo #=> 2
514Worker.foo #=> 3
515
516module ModuleExample
517 def foo
518 "foo"
519 end
520end
521
522# Подключение модуля в класс добавляет его методы в экземпляр класса
523# Расширение модуля добавляет его методы в сам класс
524
525class Person
526 include ModuleExample
527end
528
529class Book
530 extend ModuleExample
531end
532
533Person.foo # => undefined method 'foo' for Person:Class
534Person.new.foo # => 'foo'
535Book.foo # => 'foo'
536Book.new.foo # => undefined method 'foo' for Book
537
538
539# Обработка исключений
540
541# Создание пользовательского типа исключения
542class MyException < Exception
543end
544
545# Ещё одного
546class MyAnotherException < Exception; end
547
548ex = begin
549 raise MyException.new
550rescue ex1 : IndexError
551 "ex1"
552rescue ex2 : MyException | MyAnotherException
553 "ex2"
554rescue ex3 : Exception
555 "ex3"
556rescue ex4 # без указания конкретного типа исключения будут "отлавливаться" все
557 "ex4"
558end
559
560ex #=> "ex2"