--- filename: learnruby-ua.rb contributors: - ["David Underwood", "http://theflyingdeveloper.com"] - ["Joel Walden", "http://joelwalden.net"] - ["Luke Holder", "http://twitter.com/lukeholder"] - ["Tristan Hume", "http://thume.ca/"] - ["Nick LaMuro", "https://github.com/NickLaMuro"] - ["Marcos Brizeno", "http://www.about.me/marcosbrizeno"] - ["Ariel Krakowski", "http://www.learneroo.com"] - ["Dzianis Dashkevich", "https://github.com/dskecse"] - ["Levi Bostian", "https://github.com/levibostian"] - ["Rahil Momin", "https://github.com/iamrahil"] - ["Gabriel Halley", "https://github.com/ghalley"] - ["Persa Zula", "http://persazula.com"] - ["Jake Faris", "https://github.com/farisj"] translators: - ["Serhii Maksymchuk", "https://github.com/Serg-Maximchuk"] --- Rubi — це інтерпретована, повністю об'єктно-орієнтована мова програмування з чіткою динамічною типізацією. ```ruby # Це коментар =begin Це багаторядковий коментар Ніхто їх не використовує Тобі теж не варто =end # В першу чергу: все являється об’єктом. # Числа це об’єкти 3.class #=> Fixnum 3.to_s #=> "3" # Базова арифметика 1 + 1 #=> 2 8 - 1 #=> 7 10 * 2 #=> 20 35 / 5 #=> 7 2**5 #=> 32 5 % 3 #=> 2 # Побітові оператори 3 & 5 #=> 1 3 | 5 #=> 7 3 ^ 5 #=> 6 # Арифметика це просто синтаксичний цукор # для виклику методу об’єкта 1.+(3) #=> 4 10.* 5 #=> 50 # Спеціальні значення теж об’єкти nil # еквівалентно null в інших мовах true # істина false # хиба nil.class #=> NilClass true.class #=> TrueClass false.class #=> FalseClass # Рівність 1 == 1 #=> true 2 == 1 #=> false # Нерівність 1 != 1 #=> false 2 != 1 #=> true # Окрім власне false, nil це ще одне "хибне" значення !nil #=> true !false #=> true !0 #=> false # Інші порівняння 1 < 10 #=> true 1 > 10 #=> false 2 <= 2 #=> true 2 >= 2 #=> true # Комбінований оператор порівняння 1 <=> 10 #=> -1 10 <=> 1 #=> 1 1 <=> 1 #=> 0 # Логічні оператори true && false #=> false true || false #=> true !true #=> false # Є альтернативні версії логічних операторів з набагато меншим # прецедентом. Вони використовуються в конструкціях з контролем # виконання ланцюга виразів які повертають булевий результат. # `do_something_else` викликається лише якщо `do_something` повертає true. do_something() and do_something_else() # `log_error` викликається лише якщо `do_something` повертає false. do_something() or log_error() # Strings це об’єкти 'Я — рядок'.class #=> String "Я теж рядок".class #=> String placeholder = 'використовувати інтерполяцію рядків' "Я можу #{placeholder} коли користуюсь рядками в подвійних лапках" #=> "Я можу використовувати інтерполяцію рядків коли користуюсь рядками в подвійних лапках" # Варто надавати перевагу рядкам в одинарних лапках де це можливо # Рядки в подвійних лапках викликають додаткові внутрішні обчислення # Об’єднуйте рядки, але не з числами 'hello ' + 'world' #=> "hello world" 'hello ' + 3 #=> TypeError: can't convert Fixnum into String 'hello ' + 3.to_s #=> "hello 3" # Об’єднуйте рядки з операторами 'hello ' * 3 #=> "hello hello hello " # Додавання до рядка 'hello' << ' world' #=> "hello world" # Вивести рядок з переходом на новий рядок вкінці puts "Я надрукований!" #=> Я надрукований! #=> nil # Вивести рядок без переходу на новий print "Я надрукований!" #=> Я надрукований! => nil # Змінні x = 25 #=> 25 x #=> 25 # Зверніть увагу, оператор присвоєння повертає присвоєне значення # Отже можна робити одночасне присвоєння кількох змінних: x = y = 10 #=> 10 x #=> 10 y #=> 10 # Для назв змінних використовується зміїний_регістр snake_case = true # Використовуйте назви змінних які їх характеризують path_to_project_root = '/good/name/' path = '/bad/name/' # Символи (теж об’єкти) # Символи є незмінними константами багаторазового використання, внутрішньо # представлені цілочисельним значенням. Вони часто використовуються замість # рядків щоб ефективно передати конкретні, значущі значення. :pending.class #=> Symbol status = :pending status == :pending #=> true status == 'pending' #=> false status == :approved #=> false # Масиви # Ось масив array = [1, 2, 3, 4, 5] #=> [1, 2, 3, 4, 5] # Масиви можуть містити елементи різних типів [1, 'hello', false] #=> [1, "hello", false] # Масиви можуть бути проіндексовані # З початку array[0] #=> 1 array.first #=> 1 array[12] #=> nil # Як і арифметика, доступ до елемента масиву у вигляді [індекс] — це лише # синтаксичний цукор виклику методу [] об’єкта array.[] 0 #=> 1 array.[] 12 #=> nil # З кінця array[-1] #=> 5 array.last #=> 5 # З початковим індексом та довжиною array[2, 3] #=> [3, 4, 5] # Реверс масиву a=[1,2,3] a.reverse! #=> [3,2,1] # Елементи масиву за діапазоном індексів array[1..3] #=> [2, 3, 4] # Додавати елементи в масив можна так: array << 6 #=> [1, 2, 3, 4, 5, 6] # Або так: array.push(6) #=> [1, 2, 3, 4, 5, 6] # Перевірити чи масив містить елемент array.include?(1) #=> true # Хеш — це масив пар ключ/значення. # Хеш оголошується з використанням фігурних дужок: hash = { 'color' => 'green', 'number' => 5 } hash.keys #=> ['color', 'number'] # Значення в хеші може бути швидко знайдене за ключем: hash['color'] #=> 'green' hash['number'] #=> 5 # Запит значення за неіснуючим ключем повертає nil: hash['nothing here'] #=> nil # Починаючи з Ruby 1.9 з’явився спеціальний синтаксис при використанні # символів в якості ключів: new_hash = { defcon: 3, action: true } new_hash.keys #=> [:defcon, :action] # Перевірка наявності ключів та значень в хеші new_hash.key?(:defcon) #=> true new_hash.value?(3) #=> true # Хеші та масиви — перелічувальні типи даних # Вони мають багато корисних методів, такі як each, map, count, та інші. # Оператор вибору "if" if true 'якщо' elsif false 'інакше якщо, опціонально' else 'інакше, також опціонально' end # Оператор циклу "for" for counter in 1..5 puts "ітерація #{counter}" end #=> ітерація 1 #=> ітерація 2 #=> ітерація 3 #=> ітерація 4 #=> ітерація 5 # Проте, ніхто не використовує "for" в циклах. # Замість цього варто використовувати метод "each" і передати йому блок. # Блок — це відокремлений код який можна передати в метод, наприклад в "each". # Це аналог лямбда-виразів, анонімних функцій або замикань в інших мовах програмування. # Метод "each" для діапазону запускає блок один раз для кожного елементу діапазону. # Лічильник передається блоку в якості аргументу. # Виклик методу "each" з блоком виглядає наступним чином: (1..5).each do |counter| puts "ітерація #{counter}" end #=> ітерація 1 #=> ітерація 2 #=> ітерація 3 #=> ітерація 4 #=> ітерація 5 # Також можна загорнути блок в фігурні дужки: (1..5).each { |counter| puts "ітерація #{counter}" } # Вміст структур даних також може бути ітерований використовуючи метод "each": array.each do |element| puts "#{element} є елементом масиву" end hash.each do |key, value| puts "#{key} є #{value}" end # Якщо є необхідність індексувати ітерацію, можна використати метод "each_with_index": array.each_with_index do |element, index| puts "#{element} під номером #{index} в масиві" end # Оператор циклу "while" counter = 1 while counter <= 5 do puts "ітерація #{counter}" counter += 1 end #=> ітерація 1 #=> ітерація 2 #=> ітерація 3 #=> ітерація 4 #=> ітерація 5 # Є й інші корисні функції для циклів, такі як "map", "reduce", # "inject" та інші. Наприклад "map" в циклі проходить по масиву, # виконує над елементами операцію(-ї) в блоці і повертає абсолютно # новий масив. array = [1,2,3,4,5] doubled = array.map do |element| element * 2 end puts doubled #=> [2,4,6,8,10] puts array #=> [1,2,3,4,5] # Оператор множинного вибору grade = 'B' case grade when 'A' puts 'Відмінно!' when 'B' puts 'Пощастить наступного разу' when 'C' puts 'Ти можеш краще' when 'D' puts 'Майже четвірка' when 'E' puts 'Випросив' when 'F' puts 'Не здав!' else puts 'Інша система оцінювання, так?' end #=> "Пощастить наступного разу" # Оператор "case" також може використовувати діапазон: grade = 82 case grade when 90..100 puts 'Ура!' when 80...90 puts 'Хороша робота' when 60...80 puts 'Ну, хоч щось' else puts 'Не здав!' end #=> "Хороша робота" # Обробка вийнятків: begin # код з можливим вийнятком raise NoMemoryError, 'Ви використали всю пам’ять.' rescue NoMemoryError => exception_variable puts 'Помилка NoMemoryError', exception_variable rescue RuntimeError => other_exception_variable puts 'Помилка RuntimeError' else puts 'Цей код запуститься якщо вийнятків не було взагалі' ensure puts 'Цей код запуститься в будь-якому випадку' 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 'привіт світ' } # { # привіт світ # } # Ви можете передати блок у функцію # "&" позначає посилання на блок def guests(&block) block.call 'some_argument' end # Ви можете передати список аргументів, вони будуть перетворені в масив # Для цього існує оператор ("*") def guests(*array) array.each { |guest| puts guest } end # Якщо метод повертає масив, ви можете використати деструктуризацію def foods ['млинці', 'бутерброд', 'лазанья'] end breakfast, lunch, dinner = foods breakfast #=> 'млинці' dinner #=> 'лазанья' # Зазвичай методи які повертають булевий результат закінчуються знаком питання 5.even? # false 5.odd? # true # І якщо метод закінчується знаком оклику, то він робить щось деструктивне # типу зміни отриманого аргументу. Багато методів мають версію з "!" які # змінюють аргумент, та без "!" що повертають новий об’єкт company_name = "Дандер Міфлін" company_name.upcase #=> "ДАНДЕР МІФЛІН" company_name #=> "Дандер Міфлін" company_name.upcase! # змінна company_name зміниться цього разу! company_name #=> "ДАНДЕР МІФЛІН" # Клас оголошується ключовим словом class class Human # Змінна класу. Вона поширюється на всі екземпляри цього класу. @@species = 'Homo sapiens' # Основний ініціалізатор def initialize(name, age = 0) # Призначення аргументу "name" до однойменної змінної екземпляру @name = name # Якщо аргумент "age" не заданий, то йому буде присвоєно дефолтне значення # зі списку аргументів @age = age end # Метод-сетер def name=(name) @name = name end # Метод-ґетер def name @name end # Функціональність вище може бути інкапсульована використовуючи метод attr_accessor: attr_accessor :name # Ґетери і сетери можуть бути створені індивідуально, наприклад: attr_reader :name attr_writer :name # Метод класу позначається ключовим словом "self", щоб відрізнити від # методів екземпляра класу. # Він може бути викликаний лише в класі, але не в екземплярі. def self.say(msg) puts msg end def species @@species end end # Ініціалізуємо клас jim = Human.new('Джим Галперт') dwight = Human.new('Дуайт Шрут') # Викличемо кілька методів jim.species #=> "Homo sapiens" jim.name #=> "Джим Галперт" jim.name = "Джим Галперт II" #=> "Джим Галперт II" jim.name #=> "Джим Галперт II" dwight.species #=> "Homo sapiens" dwight.name #=> "Дуайт Шрут" # Викликати метод класу Human.say('Привіт') #=> "Привіт" # Область видимості змінних визначається способом оголошення імені змінної. # Змінні, що починаються на "$" мають глобальну область видимості. $var = "Я глобальна змінна" defined? $var #=> "global-variable" # Зміннні, що опчинають на "@" мають область видимості екзкмпляра @var = "Я змінна екземпляра" defined? @var #=> "instance-variable" # Змінні, що починаються на "@@" мають область видимості класу @@var = "Я змінна класу" defined? @@var #=> "class variable" # Змінні, що починаються з великої букви, є константами Var = "Я константа" defined? Var #=> "constant" # Клас теж об’єкт. Тому клас може мати змінні екземпляра. # Змінна класу поширюється між класом і всіма його нащадками. # Базовий клас 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 # 2 # Змінна екземпляра класу не поширюється між класами-нащадками. class Human @bar = 0 def self.bar @bar end def self.bar=(value) @bar = value end end class Doctor < Human end Human.bar # 0 Doctor.bar # nil module ModuleExample def foo 'foo' end end # Включення модулів додає їхні методи до екземплярів класу # Розширення модулів додає їхні методи в сам клас class Person include ModuleExample end class Book extend ModuleExample end Person.foo # => NoMethodError: undefined method `foo' for Person:Class Person.new.foo # => 'foo' Book.foo # => 'foo' Book.new.foo # => NoMethodError: undefined method `foo' # Колбек виконується при включенні і розширенні модуля module ConcernExample def self.included(base) base.extend(ClassMethods) base.send(:include, InstanceMethods) end module ClassMethods def bar 'bar' end end module InstanceMethods def qux 'qux' end end end class Something include ConcernExample end Something.bar # => 'bar' Something.qux # => NoMethodError: undefined method `qux' Something.new.bar # => NoMethodError: undefined method `bar' Something.new.qux # => 'qux' ``` ## Додаткові ресурси - [An Interactive Tutorial for Ruby](https://rubymonk.com/) - Learn Ruby through a series of interactive tutorials. - [Official Documentation](http://ruby-doc.org/core) - [Ruby from other languages](https://www.ruby-lang.org/en/documentation/ruby-from-other-languages/) - [Programming Ruby](http://www.amazon.com/Programming-Ruby-1-9-2-0-Programmers/dp/1937785491/) - An older [free edition](http://ruby-doc.com/docs/ProgrammingRuby/) is available online. - [Ruby Style Guide](https://github.com/bbatsov/ruby-style-guide) - A community-driven Ruby coding style guide. - [Try Ruby](https://try.ruby-lang.org/) - Learn the basic of Ruby programming language, interactive in the browser.