LEARN X · ЗА 15 МИН
Ruby
Весь Ruby на одной странице: вывод, числа, строки, массивы, хеши, методы, блоки, классы, модули и идиомы — в плотно закомментированном коде.
Ruby — динамический, полностью объектно-ориентированный язык с лаконичным синтаксисом, где почти всё является выражением и возвращает значение. Ниже — весь язык в одном плотно закомментированном файле: читай комментарии # и результаты # => ....
Вывод и комментарии
# Это однострочный комментарий
=begin
А это многострочный
комментарий между =begin и =end
=end
puts "Привет" # puts печатает строку и добавляет перевод строки
print "без " # print печатает БЕЗ перевода строки
print "переноса\n"
p "строка" # p выводит "отладочно" (с кавычками) и возвращает объект
# p "строка" # => "строка" (видно кавычки)
puts 42 # числа печатаются как есть # => 42
Переменные и числа
Типизация динамическая: тип определяется значением, объявлять не нужно.
x = 10 # целое (Integer)
y = 3.14 # дробное (Float)
name = "Ruby" # строка (String)
flag = true # логическое (true / false)
nothing = nil # "ничего" (аналог null)
puts 7 + 2 # => 9
puts 7 - 2 # => 5
puts 7 * 2 # => 14
puts 7 / 2 # => 3 (целочисленное деление)
puts 7 % 2 # => 1 (остаток)
puts 7.0 / 2 # => 3.5 (хотя бы один Float — дробный результат)
puts 2 ** 10 # => 1024 (возведение в степень)
puts 10.class # => Integer (у всего есть класс)
puts "5".to_i # => 5 (строка -> число)
puts 5.to_s # => "5" (число -> строка)
Строки
a = "мир"
puts "Привет, #{a}!" # интерполяция #{} => Привет, мир!
puts 'без #{a}' # одинарные кавычки НЕ интерполируют => без #{a}
puts "abc".upcase # => ABC
puts "ABC".downcase # => abc
puts " trim ".strip # => "trim" (убрать пробелы по краям)
puts "ruby".length # => 4
puts "ruby".reverse # => ybur
puts "ruby".include?("ub") # => true
puts "a,b,c".split(",").inspect # => ["a", "b", "c"]
puts "ха" * 3 # => хахаха (повтор строки)
puts "раз" + " " + "два" # => раз два (конкатенация)
# Символы — неизменяемые "имена", легче строк, удобны как ключи
status = :active # это Symbol, а не String
puts status # => active
puts status.class # => Symbol
Условия
Почти всё — выражение и возвращает значение. Ложны только false и nil; 0 и пустая строка — истинны.
age = 18
if age >= 18
puts "совершеннолетний"
elsif age >= 14
puts "подросток"
else
puts "ребёнок"
end
# unless — это "если НЕ"
puts "гость" unless age >= 18 # выполнится, только если условие ложно
# постфиксная форма — условие в конце строки
puts "взрослый" if age >= 18
# case / when — выбор по значению
grade = 4
result = case grade
when 5 then "отлично"
when 4 then "хорошо"
when 3 then "удовлетворительно"
else "неуд"
end
puts result # => хорошо
# тернарный оператор
puts age >= 18 ? "можно" : "нельзя" # => можно
Массивы
arr = [1, 2, 3, 4, 5]
puts arr[0] # => 1 (первый элемент)
puts arr[-1] # => 5 (последний элемент)
puts arr[1..3].inspect # => [2, 3, 4] (срез по диапазону)
arr.push(6) # добавить в конец
arr << 7 # тот же push, оператор-лопата
puts arr.inspect # => [1, 2, 3, 4, 5, 6, 7]
puts arr.first # => 1
puts arr.last # => 7
puts arr.length # => 7
puts arr.sum # => 28
puts [3, 1, 2].sort.inspect # => [1, 2, 3]
# Итерация и преобразования
[1, 2, 3].each { |n| print n } # => 123 (просто перебор)
puts
puts [1, 2, 3].map { |n| n * 10 }.inspect # => [10, 20, 30] (преобразовать каждый)
puts [1, 2, 3, 4].select { |n| n.even? }.inspect # => [2, 4] (отфильтровать)
puts [1, 2, 3, 4].reject { |n| n.even? }.inspect # => [1, 3] (выбросить подходящие)
puts [1, 2, 3, 4].reduce(0) { |sum, n| sum + n } # => 10 (свернуть в одно)
Хеши
# Хеш — набор пар ключ => значение
person = { "name" => "Аня", "age" => 25 }
puts person["name"] # => Аня
# Чаще ключами делают символы — короткий синтаксис key:
user = { name: "Боб", role: :admin, age: 30 }
puts user[:name] # => Боб
puts user[:role] # => admin
user[:email] = "[email protected]" # добавить пару
puts user.keys.inspect # => [:name, :role, :age, :email]
puts user.values.inspect # => ["Боб", :admin, 30, "[email protected]"]
puts user.key?(:name) # => true
puts user.fetch(:age) # => 30 (как [], но кидает ошибку, если ключа нет)
# Перебор хеша
user.each { |key, value| puts "#{key} = #{value}" }
# => name = Боб
# => role = admin ...
Диапазоны и циклы
(1..5).each { |i| print i } # => 12345 (.. включает конец)
puts
(1...5).each { |i| print i } # => 1234 (... НЕ включает конец)
puts
puts (1..5).to_a.inspect # => [1, 2, 3, 4, 5]
3.times { |i| print i } # => 012 (повторить N раз)
puts
1.upto(3) { |i| print i } # => 123 (от и до включительно)
puts
3.downto(1) { |i| print i } # => 321 (в обратную сторону)
puts
# while — цикл с условием
i = 0
while i < 3
print i
i += 1
end # => 012
puts
# for тоже есть, но в Ruby редок — обычно используют each/times
for n in [10, 20] do print n end # => 1020
puts
Методы
# Метод объявляется через def ... end
def greet(name)
"Привет, #{name}!" # последнее выражение — это и есть результат (return необязателен)
end
puts greet("Ruby") # => Привет, Ruby!
# Аргумент по умолчанию
def power(base, exp = 2)
base ** exp
end
puts power(5) # => 25 (exp по умолчанию = 2)
puts power(5, 3) # => 125
# Явный return (например, для раннего выхода)
def sign(n)
return "ноль" if n.zero?
n > 0 ? "плюс" : "минус"
end
puts sign(-7) # => минус
# splat * — произвольное число аргументов собирается в массив
def total(*numbers)
numbers.sum
end
puts total(1, 2, 3, 4) # => 10
# Метод с ? обычно возвращает true/false, с ! — меняет объект на месте
puts 4.even? # => true
Блоки и итераторы
Блок — кусок кода, передаваемый методу. Это сердце Ruby.
# Две формы блоков: {} для коротких и do..end для многострочных
[1, 2, 3].each { |n| puts n } # фигурные скобки
[1, 2, 3].each do |n| # do..end — то же самое
puts n * 2
end
# yield вызывает переданный блок изнутри метода
def twice
yield # выполнить блок
yield # ещё раз
end
twice { puts "ку" } # => ку (дважды)
# Можно передавать значение в блок
def with_tax(price)
yield(price * 1.2)
end
with_tax(100) { |total| puts "Итого: #{total}" } # => Итого: 120.0
# block_given? — проверить, передан ли блок
def maybe
return "нет блока" unless block_given?
yield
end
puts maybe # => нет блока
# &block — захватить блок в именованный параметр (объект Proc)
def run(&block)
block.call
end
run { puts "запущено" } # => запущено
Классы и ООП
class Dog
# attr_accessor создаёт геттер И сеттер для @name
attr_accessor :name
attr_reader :breed # только чтение
# initialize — конструктор, вызывается при Dog.new
def initialize(name, breed)
@name = name # @ — переменная экземпляра (живёт в объекте)
@breed = breed
end
# обычный метод экземпляра
def bark
"#{@name} говорит: Гав!"
end
end
rex = Dog.new("Рекс", "овчарка") # создать объект
puts rex.bark # => Рекс говорит: Гав!
puts rex.name # => Рекс (геттер)
rex.name = "Шарик" # сеттер (благодаря attr_accessor)
puts rex.name # => Шарик
puts rex.breed # => овчарка (только чтение)
puts rex.is_a?(Dog) # => true
Наследование и модули
class Animal
def initialize(name)
@name = name
end
def speak
"#{@name} издаёт звук"
end
end
# Cat наследует от Animal через <
class Cat < Animal
def speak
# super вызывает одноимённый метод родителя
super + ", а именно: Мяу"
end
end
puts Cat.new("Барсик").speak # => Барсик издаёт звук, а именно: Мяу
# Модуль — набор методов для подмешивания (mixin), не создаёт объектов
module Greetable
def hello
"Привет, я #{@name}"
end
end
class Person
include Greetable # подмешать методы модуля в класс
def initialize(name)
@name = name
end
end
puts Person.new("Лена").hello # => Привет, я Лена
Исключения
begin
result = 10 / 0 # вызовет ZeroDivisionError
rescue ZeroDivisionError => e # перехватить конкретную ошибку
puts "Ошибка: #{e.message}" # => Ошибка: divided by 0
rescue => e # перехватить любую прочую ошибку
puts "Что-то пошло не так"
ensure
puts "ensure выполнится в любом случае" # всегда, как finally
end
# raise — выбросить своё исключение
def withdraw(amount)
raise ArgumentError, "сумма должна быть > 0" if amount <= 0
amount
end
begin
withdraw(-5)
rescue ArgumentError => e
puts e.message # => сумма должна быть > 0
end
Идиомы Ruby
# ||= присвоить, только если переменная nil или false ("мемоизация")
@cache ||= []
name = nil
name ||= "по умолчанию"
puts name # => по умолчанию
# &. — безопасный вызов: если слева nil, вернёт nil, а не упадёт с ошибкой
user = nil
puts user&.upcase.inspect # => nil (без &. была бы ошибка)
puts "Боб"&.upcase # => БОБ
# &:method — короткая запись блока { |x| x.method }
puts ["a", "bb", "c"].map(&:upcase).inspect # => ["A", "BB", "C"]
puts [1, 2, 3].map(&:to_s).inspect # => ["1", "2", "3"]
# В Ruby ВСЁ — объект, даже числа и nil, у всего есть методы
puts 5.times.to_a.inspect # => [0, 1, 2, 3, 4]
puts nil.to_s.inspect # => "" (даже у nil есть методы)
puts (-7).abs # => 7
puts 1.is_a?(Object) # => true (число — тоже объект)
# Множественное присваивание
a, b = 1, 2
a, b = b, a # обмен значениями без временной переменной
puts "#{a} #{b}" # => 2 1