From 3e8b76e7f9ec06ccc6b8ea0a64c88c08792702be Mon Sep 17 00:00:00 2001 From: Maksim Litvinov Date: Wed, 27 Dec 2023 14:13:03 +0300 Subject: [PATCH] new structure --- .../10-basics/10-hello-world/ru/EXERCISE.md | 6 + modules/10-basics/10-hello-world/ru/README.md | 16 +++ modules/10-basics/10-hello-world/ru/data.yml | 2 + .../ru/EXERCISE.md | 8 ++ .../15-ruby-as-a-second-language/ru/README.md | 76 ++++++++++++ .../15-ruby-as-a-second-language/ru/data.yml | 4 + .../20-everything-is-object/ru/EXERCISE.md | 11 ++ .../20-everything-is-object/ru/README.md | 61 ++++++++++ .../20-everything-is-object/ru/data.yml | 2 + .../25-first-function/ru/EXERCISE.md | 7 ++ .../10-basics/25-first-function/ru/README.md | 45 +++++++ .../10-basics/25-first-function/ru/data.yml | 2 + .../10-basics/27-conditions/ru/EXERCISE.md | 7 ++ modules/10-basics/27-conditions/ru/README.md | 60 ++++++++++ modules/10-basics/27-conditions/ru/data.yml | 2 + modules/10-basics/30-if/ru/EXERCISE.md | 16 +++ modules/10-basics/30-if/ru/README.md | 23 ++++ modules/10-basics/30-if/ru/data.yml | 2 + modules/10-basics/33-if-extra/ru/EXERCISE.md | 16 +++ modules/10-basics/33-if-extra/ru/README.md | 40 +++++++ modules/10-basics/33-if-extra/ru/data.yml | 2 + modules/10-basics/35-strings/ru/EXERCISE.md | 9 ++ modules/10-basics/35-strings/ru/README.md | 50 ++++++++ modules/10-basics/35-strings/ru/data.yml | 7 ++ modules/10-basics/40-blocks/ru/EXERCISE.md | 9 ++ modules/10-basics/40-blocks/ru/README.md | 112 ++++++++++++++++++ modules/10-basics/40-blocks/ru/data.yml | 2 + modules/10-basics/45-iteration/ru/EXERCISE.md | 7 ++ modules/10-basics/45-iteration/ru/README.md | 32 +++++ modules/10-basics/45-iteration/ru/data.yml | 2 + .../47-iteration-over-string/ru/EXERCISE.md | 7 ++ .../47-iteration-over-string/ru/README.md | 34 ++++++ .../47-iteration-over-string/ru/data.yml | 2 + modules/10-basics/50-datatypes/ru/EXERCISE.md | 11 ++ modules/10-basics/50-datatypes/ru/README.md | 61 ++++++++++ modules/10-basics/50-datatypes/ru/data.yml | 2 + modules/10-basics/60-classes/ru/EXERCISE.md | 16 +++ modules/10-basics/60-classes/ru/README.md | 68 +++++++++++ modules/10-basics/60-classes/ru/data.yml | 5 + modules/10-basics/65-modules/ru/EXERCISE.md | 15 +++ modules/10-basics/65-modules/ru/README.md | 47 ++++++++ modules/10-basics/65-modules/ru/data.yml | 2 + .../ru/EXERCISE.md | 13 ++ .../67-everything-is-expression/ru/README.md | 25 ++++ .../67-everything-is-expression/ru/data.yml | 2 + modules/10-basics/70-summary/ru/EXERCISE.md | 18 +++ modules/10-basics/70-summary/ru/README.md | 27 +++++ modules/10-basics/70-summary/ru/data.yml | 2 + .../10-arrays-basics/ru/EXERCISE.md | 7 ++ .../10-arrays-basics/ru/README.md | 48 ++++++++ .../10-arrays-basics/ru/data.yml | 4 + .../20-arrays-methods/ru/EXERCISE.md | 32 +++++ .../20-arrays-methods/ru/README.md | 38 ++++++ .../20-arrays-methods/ru/data.yml | 2 + .../30-hash-basics/ru/EXERCISE.md | 14 +++ .../30-hash-basics/ru/README.md | 53 +++++++++ .../20-collections/30-hash-basics/ru/data.yml | 4 + .../40-hash-methods/ru/EXERCISE.md | 36 ++++++ .../40-hash-methods/ru/README.md | 86 ++++++++++++++ .../40-hash-methods/ru/data.yml | 2 + .../50-destructuring/ru/EXERCISE.md | 19 +++ .../50-destructuring/ru/README.md | 70 +++++++++++ .../50-destructuring/ru/data.yml | 2 + .../ru/EXERCISE.md | 14 +++ .../ru/README.md | 36 ++++++ .../10-functions-definition-splat/ru/data.yml | 2 + .../ru/EXERCISE.md | 14 +++ .../ru/README.md | 28 +++++ .../ru/data.yml | 2 + .../30-functions-pipeline/ru/EXERCISE.md | 26 ++++ .../30-functions-pipeline/ru/README.md | 49 ++++++++ .../30-functions-pipeline/ru/data.yml | 2 + .../ru/EXERCISE.md | 12 ++ .../ru/README.md | 43 +++++++ .../ru/data.yml | 2 + .../ru/EXERCISE.md | 13 ++ .../ru/README.md | 31 +++++ .../ru/data.yml | 2 + .../60-functions-yield/ru/EXERCISE.md | 13 ++ .../60-functions-yield/ru/README.md | 73 ++++++++++++ .../60-functions-yield/ru/data.yml | 2 + .../10-each-with-object/ru/EXERCISE.md | 14 +++ .../10-each-with-object/ru/README.md | 25 ++++ .../40-arrays/10-each-with-object/ru/data.yml | 2 + .../40-arrays-as-sets/ru/EXERCISE.md | 24 ++++ .../40-arrays/40-arrays-as-sets/ru/README.md | 53 +++++++++ .../40-arrays/40-arrays-as-sets/ru/data.yml | 2 + .../50-arrays-definitions/ru/EXERCISE.md | 14 +++ .../50-arrays-definitions/ru/README.md | 44 +++++++ .../50-arrays-definitions/ru/data.yml | 2 + modules/50-hash/10-hash-fetch/ru/EXERCISE.md | 16 +++ modules/50-hash/10-hash-fetch/ru/README.md | 56 +++++++++ modules/50-hash/10-hash-fetch/ru/data.yml | 6 + modules/50-hash/20-hash-new/ru/EXERCISE.md | 23 ++++ modules/50-hash/20-hash-new/ru/README.md | 55 +++++++++ modules/50-hash/20-hash-new/ru/data.yml | 4 + 96 files changed, 2116 insertions(+) create mode 100644 modules/10-basics/10-hello-world/ru/EXERCISE.md create mode 100644 modules/10-basics/10-hello-world/ru/README.md create mode 100644 modules/10-basics/10-hello-world/ru/data.yml create mode 100644 modules/10-basics/15-ruby-as-a-second-language/ru/EXERCISE.md create mode 100644 modules/10-basics/15-ruby-as-a-second-language/ru/README.md create mode 100644 modules/10-basics/15-ruby-as-a-second-language/ru/data.yml create mode 100644 modules/10-basics/20-everything-is-object/ru/EXERCISE.md create mode 100644 modules/10-basics/20-everything-is-object/ru/README.md create mode 100644 modules/10-basics/20-everything-is-object/ru/data.yml create mode 100644 modules/10-basics/25-first-function/ru/EXERCISE.md create mode 100644 modules/10-basics/25-first-function/ru/README.md create mode 100644 modules/10-basics/25-first-function/ru/data.yml create mode 100644 modules/10-basics/27-conditions/ru/EXERCISE.md create mode 100644 modules/10-basics/27-conditions/ru/README.md create mode 100644 modules/10-basics/27-conditions/ru/data.yml create mode 100644 modules/10-basics/30-if/ru/EXERCISE.md create mode 100644 modules/10-basics/30-if/ru/README.md create mode 100644 modules/10-basics/30-if/ru/data.yml create mode 100644 modules/10-basics/33-if-extra/ru/EXERCISE.md create mode 100644 modules/10-basics/33-if-extra/ru/README.md create mode 100644 modules/10-basics/33-if-extra/ru/data.yml create mode 100644 modules/10-basics/35-strings/ru/EXERCISE.md create mode 100644 modules/10-basics/35-strings/ru/README.md create mode 100644 modules/10-basics/35-strings/ru/data.yml create mode 100644 modules/10-basics/40-blocks/ru/EXERCISE.md create mode 100644 modules/10-basics/40-blocks/ru/README.md create mode 100644 modules/10-basics/40-blocks/ru/data.yml create mode 100644 modules/10-basics/45-iteration/ru/EXERCISE.md create mode 100644 modules/10-basics/45-iteration/ru/README.md create mode 100644 modules/10-basics/45-iteration/ru/data.yml create mode 100644 modules/10-basics/47-iteration-over-string/ru/EXERCISE.md create mode 100644 modules/10-basics/47-iteration-over-string/ru/README.md create mode 100644 modules/10-basics/47-iteration-over-string/ru/data.yml create mode 100644 modules/10-basics/50-datatypes/ru/EXERCISE.md create mode 100644 modules/10-basics/50-datatypes/ru/README.md create mode 100644 modules/10-basics/50-datatypes/ru/data.yml create mode 100644 modules/10-basics/60-classes/ru/EXERCISE.md create mode 100644 modules/10-basics/60-classes/ru/README.md create mode 100644 modules/10-basics/60-classes/ru/data.yml create mode 100644 modules/10-basics/65-modules/ru/EXERCISE.md create mode 100644 modules/10-basics/65-modules/ru/README.md create mode 100644 modules/10-basics/65-modules/ru/data.yml create mode 100644 modules/10-basics/67-everything-is-expression/ru/EXERCISE.md create mode 100644 modules/10-basics/67-everything-is-expression/ru/README.md create mode 100644 modules/10-basics/67-everything-is-expression/ru/data.yml create mode 100644 modules/10-basics/70-summary/ru/EXERCISE.md create mode 100644 modules/10-basics/70-summary/ru/README.md create mode 100644 modules/10-basics/70-summary/ru/data.yml create mode 100644 modules/20-collections/10-arrays-basics/ru/EXERCISE.md create mode 100644 modules/20-collections/10-arrays-basics/ru/README.md create mode 100644 modules/20-collections/10-arrays-basics/ru/data.yml create mode 100644 modules/20-collections/20-arrays-methods/ru/EXERCISE.md create mode 100644 modules/20-collections/20-arrays-methods/ru/README.md create mode 100644 modules/20-collections/20-arrays-methods/ru/data.yml create mode 100644 modules/20-collections/30-hash-basics/ru/EXERCISE.md create mode 100644 modules/20-collections/30-hash-basics/ru/README.md create mode 100644 modules/20-collections/30-hash-basics/ru/data.yml create mode 100644 modules/20-collections/40-hash-methods/ru/EXERCISE.md create mode 100644 modules/20-collections/40-hash-methods/ru/README.md create mode 100644 modules/20-collections/40-hash-methods/ru/data.yml create mode 100644 modules/20-collections/50-destructuring/ru/EXERCISE.md create mode 100644 modules/20-collections/50-destructuring/ru/README.md create mode 100644 modules/20-collections/50-destructuring/ru/data.yml create mode 100644 modules/25-functions/10-functions-definition-splat/ru/EXERCISE.md create mode 100644 modules/25-functions/10-functions-definition-splat/ru/README.md create mode 100644 modules/25-functions/10-functions-definition-splat/ru/data.yml create mode 100644 modules/25-functions/20-functions-object-as-last-argument/ru/EXERCISE.md create mode 100644 modules/25-functions/20-functions-object-as-last-argument/ru/README.md create mode 100644 modules/25-functions/20-functions-object-as-last-argument/ru/data.yml create mode 100644 modules/25-functions/30-functions-pipeline/ru/EXERCISE.md create mode 100644 modules/25-functions/30-functions-pipeline/ru/README.md create mode 100644 modules/25-functions/30-functions-pipeline/ru/data.yml create mode 100644 modules/25-functions/40-functions-blocks-as-objects/ru/EXERCISE.md create mode 100644 modules/25-functions/40-functions-blocks-as-objects/ru/README.md create mode 100644 modules/25-functions/40-functions-blocks-as-objects/ru/data.yml create mode 100644 modules/25-functions/50-functions-symbols-to-blocks/ru/EXERCISE.md create mode 100644 modules/25-functions/50-functions-symbols-to-blocks/ru/README.md create mode 100644 modules/25-functions/50-functions-symbols-to-blocks/ru/data.yml create mode 100644 modules/25-functions/60-functions-yield/ru/EXERCISE.md create mode 100644 modules/25-functions/60-functions-yield/ru/README.md create mode 100644 modules/25-functions/60-functions-yield/ru/data.yml create mode 100644 modules/40-arrays/10-each-with-object/ru/EXERCISE.md create mode 100644 modules/40-arrays/10-each-with-object/ru/README.md create mode 100644 modules/40-arrays/10-each-with-object/ru/data.yml create mode 100644 modules/40-arrays/40-arrays-as-sets/ru/EXERCISE.md create mode 100644 modules/40-arrays/40-arrays-as-sets/ru/README.md create mode 100644 modules/40-arrays/40-arrays-as-sets/ru/data.yml create mode 100644 modules/40-arrays/50-arrays-definitions/ru/EXERCISE.md create mode 100644 modules/40-arrays/50-arrays-definitions/ru/README.md create mode 100644 modules/40-arrays/50-arrays-definitions/ru/data.yml create mode 100644 modules/50-hash/10-hash-fetch/ru/EXERCISE.md create mode 100644 modules/50-hash/10-hash-fetch/ru/README.md create mode 100644 modules/50-hash/10-hash-fetch/ru/data.yml create mode 100644 modules/50-hash/20-hash-new/ru/EXERCISE.md create mode 100644 modules/50-hash/20-hash-new/ru/README.md create mode 100644 modules/50-hash/20-hash-new/ru/data.yml diff --git a/modules/10-basics/10-hello-world/ru/EXERCISE.md b/modules/10-basics/10-hello-world/ru/EXERCISE.md new file mode 100644 index 0000000..686a78e --- /dev/null +++ b/modules/10-basics/10-hello-world/ru/EXERCISE.md @@ -0,0 +1,6 @@ + +Наберите в редакторе код из задания символ в символ и нажмите «Проверить». + +```ruby +puts 'Hello, World!' +``` diff --git a/modules/10-basics/10-hello-world/ru/README.md b/modules/10-basics/10-hello-world/ru/README.md new file mode 100644 index 0000000..cd007e9 --- /dev/null +++ b/modules/10-basics/10-hello-world/ru/README.md @@ -0,0 +1,16 @@ + +По традиции начнем с написания программы 'Hello, World!'. Эта программа будет выводить на экран текст: + +```ruby +# В конце инструкции нет символа ; +# В Ruby не ставятся ; практически никогда +puts 'Hello, World!' +``` + +`puts()` — функция печати на экран. В Ruby функции могут вызываться как со скобками, так и без них — разница только в способе записи. Код выше можно было бы записать так: + +```ruby +puts('Hello, World!') +``` + +Решение о том, ставить скобки или нет, зависит от предпочтений программиста и удобства в каждой конкретной ситуации. Это понимание придёт со временем. diff --git a/modules/10-basics/10-hello-world/ru/data.yml b/modules/10-basics/10-hello-world/ru/data.yml new file mode 100644 index 0000000..7640b7e --- /dev/null +++ b/modules/10-basics/10-hello-world/ru/data.yml @@ -0,0 +1,2 @@ +name: Привет, Мир! +tips: [] diff --git a/modules/10-basics/15-ruby-as-a-second-language/ru/EXERCISE.md b/modules/10-basics/15-ruby-as-a-second-language/ru/EXERCISE.md new file mode 100644 index 0000000..2aaaba2 --- /dev/null +++ b/modules/10-basics/15-ruby-as-a-second-language/ru/EXERCISE.md @@ -0,0 +1,8 @@ + +На данном этапе мы знаем ещё слишком мало, чтобы писать какой-то интересный код, поэтому просто скопируйте и выполните код ниже. В этом коде используется библиотека ActiveSupport, которая расширяет язык некоторыми полезными возможностями. Изначально она была создана для Rails, но используется и за его пределами. + +```ruby +# На сервере время в UTC +# Считаться будет от него +puts 1.day.ago - 1.week + 3.hours +``` diff --git a/modules/10-basics/15-ruby-as-a-second-language/ru/README.md b/modules/10-basics/15-ruby-as-a-second-language/ru/README.md new file mode 100644 index 0000000..35364c5 --- /dev/null +++ b/modules/10-basics/15-ruby-as-a-second-language/ru/README.md @@ -0,0 +1,76 @@ + +Ruby на нашем проекте дается как «второй язык» — это значит, что мы подразумеваем определенный багаж знаний у тех, кто учится ему здесь. В первую очередь это владение каким-либо другим языком программирования. Для понимания материала курса нужно иметь представление о типах данных, переменных, условных конструкциях, циклах, функциях, объектах (свойствах и методах) и лямбда-функциях (анонимных функциях). + +Изучение второго языка значительно проще первого, поэтому и структура материала сильно меняется. Здесь мы обзорно касаемся базовых конструкций для быстрого знакомства с синтаксисом, и переходим к интересным задачкам, ради которых, собственно, и изучается Ruby. + +## В чём соль? + +Ruby — динамический, строго типизированный язык с глубоким уклоном в объектно-ориентированную и функциональную парадигму программирования. + +```ruby +# Строгая типизация, число нельзя умножить на строку +4 * 'hexlet' # TypeError (String can't be coerced into Integer) +# Всё объекты +1.8.round # 2 +# Функции высшего порядка +['one', 'two'].map(&:upcase) # ["ONE", "TWO"] +``` + +Он обладает местами непривычным, но невероятно выразительным синтаксисом. Благодаря этому код на Ruby читается как английский язык и при этом остаётся компактным: + +```ruby +# Определение конечного автомата +class Job + include AASM + + aasm do + state :sleeping, initial: true + state :running, :cleaning + + event :run do + transitions from: :sleeping, to: :running + end + + event :sleep do + transitions from: [:running, :cleaning], to: :sleeping + end + end +end + +job = Job.new +job.sleeping? # true +job.may_run? # true +job.run +job.running? # true +job.may_run? # false +``` + +На Ruby создано большое количество решений, которые потом распространились по другим языкам. Самым ярким примером является фреймворк Ruby On Rails, который когда-то перевернул веб-разработку, и до сих пор является самым продвинутым бэкенд фреймворком для веб-разработки. И github.com, и gitlab.com написаны с использованием Rails. Даже этот сайт написан на Rails. + +В Ruby очень много метапрограммирования, встроенного во все части языка. Это значит, что программа может менять свое поведение прямо во время работы практически в любую сторону. Посмотрите, как происходит добавление методов во встроенные классы: + +```ruby +# Этот код можно написать в любом месте программы +class String # встроенный в Ruby класс, отвечающий за строки + def wow # определение метода + 'ruby power' + end +end + +# Вызываем метод +'hexlet'.wow # 'ruby power' +``` + +И даже так: + +```ruby +# Только для демонстрации +# Класс New наследуется от случайно выбранного +# класса One или Two, в зависимости от того, +# какой из них выберет метод sample на момент загрузки файла с классом +class New < [One, Two].sample + # тут код класса +end +``` + +На этом моменте у вас возможно возникла мысль «это же ад». Как и любой мощный инструмент, Ruby можно использовать во зло — это правда, но если посмотреть на реальное положение дел, то при правильном использовании Ruby позволяет писать невероятно выразительный код. И раз вы здесь, то давайте научимся это делать. diff --git a/modules/10-basics/15-ruby-as-a-second-language/ru/data.yml b/modules/10-basics/15-ruby-as-a-second-language/ru/data.yml new file mode 100644 index 0000000..01f44d5 --- /dev/null +++ b/modules/10-basics/15-ruby-as-a-second-language/ru/data.yml @@ -0,0 +1,4 @@ +name: Ruby, Ruby, Ruby +tips: + - | + [Ruby-комьюнити](https://t.me/hexletcommunity/9) diff --git a/modules/10-basics/20-everything-is-object/ru/EXERCISE.md b/modules/10-basics/20-everything-is-object/ru/EXERCISE.md new file mode 100644 index 0000000..c679523 --- /dev/null +++ b/modules/10-basics/20-everything-is-object/ru/EXERCISE.md @@ -0,0 +1,11 @@ + +Напечатайте на экран следующий вызов: + +```ruby +# Здесь выводятся на экран все методы строк, +# которые содержат знак вопроса в имени +# Фактически — это вывод списка предикатов +# То же самое можно сделать для любого типа +puts 'hexlet'.methods.grep(/\?/) +# Будет выведено около 20 имен +``` diff --git a/modules/10-basics/20-everything-is-object/ru/README.md b/modules/10-basics/20-everything-is-object/ru/README.md new file mode 100644 index 0000000..9e63cde --- /dev/null +++ b/modules/10-basics/20-everything-is-object/ru/README.md @@ -0,0 +1,61 @@ + +Ruby по своим возможностям и подходам в разработке близок к JavaScript и Python, но имеет свои особенности. Разработчики языка во многом опирались на Smalltalk, Lisp (это семейство языков), Perl и другие подобные языки. Это привело к интересному результату. Во-первых, Ruby — **очень** объектно-ориентированный язык. В Ruby всё есть объект, включая `nil` (это аналог `null`), и каждая операция — это вызов метода: + +```ruby +'hexlet'.reverse # telxeh +1.7.round # 2 +nil.to_i # 0 +1 + 1 # на самом деле 1.+(1) +``` + +Одна из сильных сторон Ruby – [стандартная библиотека](https://ruby-doc.org/). Она решает практически все возникающие задачи. В одних только строках 185 встроенных методов! + +*Это одна из причин, почему Ruby чаще других выигрывает в [CodeBattle](https://codebattle.hexlet.io/)* + +```ruby +# Количество методов у разных типов данных +# В примерах ниже вызывается метод methods, +# хотя кажется, что это обращение к свойству +''.methods.count # 185 +1.methods.count # 145 +[].methods.count # 191 +``` + +В Ruby всё, кроме присваивания, это вызовы методов. Такой подход позволяет переопределять буквально любое поведение: + +```ruby +# То, что на самом деле происходит, когда мы выполняем «операции» +1.+(5) # 6 +1.>(3) # false +``` + +На этом основано очень много кода, особенно библиотечного. Например, у любого объекта можно определить синтаксис, аналогичный доступу к массиву `[]`. Или можно определить операции для дат, сделав работу с ними максимально простой (как в примере прошлого урока). А вот как выглядят сеттеры у объектов: + +```ruby +# Кажется, что это прямое изменение свойства +# На самом деле это сеттер object.key=('value') +obj.key = 'value' +``` + +Все данные в Ruby — это объекты. Например `nil`, представлен классом `NilClass`, и является единственным его объектом. `true` — объект класса `TrueClass`, а `false` — объект класса `FalseClass`. У остальных типов свои классы. + +Узнать класс любого объекта можно так: + +```ruby +1.class # Integer +''.class # String +nil.class # NilClass +``` + +С другой стороны классы в Ruby — тоже объекты, у которых есть свои классы 0_o. Но это уже совсем другая история) Есть даже такая шутка (но это не шутка): в Ruby объект это класс, а класс — это объект. Почему это так – узнаем чуть позже. + +## Отладочная печать + +Иногда в работе приходится прибегать к отладочной печати и в Ruby есть несколько особенностей, о которых надо знать. Функция `puts()` выводит любые типы данных без необходимости преобразования их в строку. С другой стороны, такой вывод не останавливает выполнение и иногда это неудобно, если хочется посмотреть только первый вывод. Для таких ситуаций лучше использовать выброс исключения, которое и выведет на экран нужную информацию и прервёт выполнение. Делается это так: + +```ruby +# raise – бросить исключение +# .inspect – метод, который преобразует +# любые данные в строковое представление +raise something.inspect +``` diff --git a/modules/10-basics/20-everything-is-object/ru/data.yml b/modules/10-basics/20-everything-is-object/ru/data.yml new file mode 100644 index 0000000..50eb8ef --- /dev/null +++ b/modules/10-basics/20-everything-is-object/ru/data.yml @@ -0,0 +1,2 @@ +name: Всё есть объект +tips: [] diff --git a/modules/10-basics/25-first-function/ru/EXERCISE.md b/modules/10-basics/25-first-function/ru/EXERCISE.md new file mode 100644 index 0000000..6e22165 --- /dev/null +++ b/modules/10-basics/25-first-function/ru/EXERCISE.md @@ -0,0 +1,7 @@ + +Реализуйте функцию `double()`, которая удваивает любое переданное ей число и возвращает его: + +```ruby +double(3) # 6 +double(8) # 16 +``` diff --git a/modules/10-basics/25-first-function/ru/README.md b/modules/10-basics/25-first-function/ru/README.md new file mode 100644 index 0000000..7d66376 --- /dev/null +++ b/modules/10-basics/25-first-function/ru/README.md @@ -0,0 +1,45 @@ + +Начнем сразу с комплексного примера, включающего в себя определение функции, переменной и простые арифметические операции. Посмотрим на синтаксис и разберём некоторые интересные особенности Ruby. Ниже дано определение функции, находящей среднее арифметическое: + +```ruby +# def – определение функции +def find_average(a, b) + # создание переменной sum + sum = a + b + # преобразование к Float + sum.to_f / 2 +end + +find_average(3, 1) # 2.0 +find_average(1, 2) # 1.5 + +# Можно вызывать и так +find_average 3, 1 +find_average 1, 2 + +# ArgumentError (wrong number of arguments (given 1, expected 2) +find_average(1) +``` + +Сначала пара слов о стиле. В Ruby существует ровно один общепринятый стиль оформления кода, которого [придерживается](https://ukupat.github.io/tabs-or-spaces/) все комьюнити: + +* Отступы — два пробела +* Для определения имен переменных и функций используется snake_case +* Определение классов — CamelCase + +Определение функции начинается ключевым словом `def`, за которым идёт имя функции и её параметры. В конце добавляется `end`. В отличие от своих собратьев, в Ruby не обязательно писать ключевое слово `return`. Как и в Smalltalk, в Ruby функции всегда возвращают результат последнего вычисленного выражения. Иногда `return` всё же используется, когда нужно досрочно выйти из функции. В остальных случаях его опускают. + +Определение переменных в Ruby крайне простое. Достаточно написать имя и присвоить ему любое значение. Затем переменную можно использовать и менять как угодно и где угодно в поле её видимости. + +```ruby +sum = a + b +``` + +Необычно в примере выше может выглядеть код `sum.to_f`. Здесь происходит вызов метода `to_f()` у числа, записанного в `sum`. Для чего это нужно? В Ruby при делении целых чисел на выходе всегда получается целое число. Если при этом была дробная часть, она отбрасывается: + +```ruby +3 / 2 # 1 +3.to_f / 2 # 1.5 +``` + +По своему смыслу функция `find_average()` должна возвращать число с плавающей точкой, поэтому такое преобразование необходимо. diff --git a/modules/10-basics/25-first-function/ru/data.yml b/modules/10-basics/25-first-function/ru/data.yml new file mode 100644 index 0000000..8b8494c --- /dev/null +++ b/modules/10-basics/25-first-function/ru/data.yml @@ -0,0 +1,2 @@ +name: Первая функция +tips: [] diff --git a/modules/10-basics/27-conditions/ru/EXERCISE.md b/modules/10-basics/27-conditions/ru/EXERCISE.md new file mode 100644 index 0000000..53fe9d5 --- /dev/null +++ b/modules/10-basics/27-conditions/ru/EXERCISE.md @@ -0,0 +1,7 @@ + +Реализуйте функцию предикат `even?`, которая проверяет, является ли переданное число чётным. Функция должна возвращать `true`, если число чётное и `false`, если не чётное. Не используйте встроенные функции для определения четности: + +```ruby +even?(5) # false +even?(6) # true +``` diff --git a/modules/10-basics/27-conditions/ru/README.md b/modules/10-basics/27-conditions/ru/README.md new file mode 100644 index 0000000..9972e79 --- /dev/null +++ b/modules/10-basics/27-conditions/ru/README.md @@ -0,0 +1,60 @@ + +## Логические операторы + +Логический тип в Ruby представлен привычными значениями `true` и `false`, а также набором операторов `&&` (и), `==` (равно), `||` (или) и `!` (не): + +```ruby +true && false # false +false || true # true +``` + +В отличие от многих других языков сравнение с логическим значением в Ruby строгое, то есть `true` и `false` равны только самим себе: + +```ruby +true == 1 # false +false == nil # false +``` + +Что не отменяет возможности использовать в логических выражениях значения любых типов: + +```ruby +0 && 'one' # "one" +nil && false # nil +``` + +В Ruby только `nil` и `false` рассматриваются как *falsey*, все остальные значения в логических выражениях приводятся к `true`. + +## Значение по умолчанию + +В Ruby широко используется такой код: + +```ruby +a ||= 'что-то' +# a = a || 'что-то' +``` + +Он используется для задания значения по умолчанию. Такое возможно и почти всегда безопасно из-за очень ограниченного списка *falsey* значений. Единственное место, где этот способ не сработает – там, где `false` это допустимое значение. + +## Предикаты + +В Ruby в отличие от большинства других языков принято использовать предикаты практически для всех часто встречающихся проверок. Например, как мы обычно проверяем, что число равно нулю? С помощью сравнения с нулем. В Ruby это тоже работает, но это не Ruby Way: + +```ruby +0.zero? # true +1.zero? # false +2.positive? # true + +# чётное/нечётное +8.even? # true +8.odd? # false + +''.empty? # true +'wow'.empty? # false + +something.nil? + +# не пустой массив +items.any? +# пустой массив +items.empty? +``` diff --git a/modules/10-basics/27-conditions/ru/data.yml b/modules/10-basics/27-conditions/ru/data.yml new file mode 100644 index 0000000..2c3a791 --- /dev/null +++ b/modules/10-basics/27-conditions/ru/data.yml @@ -0,0 +1,2 @@ +name: Логический тип +tips: [] diff --git a/modules/10-basics/30-if/ru/EXERCISE.md b/modules/10-basics/30-if/ru/EXERCISE.md new file mode 100644 index 0000000..61adc39 --- /dev/null +++ b/modules/10-basics/30-if/ru/EXERCISE.md @@ -0,0 +1,16 @@ + +В Ruby встроен оператор `<=>` (spaceship). Этот оператор удобно использовать в функциях сортировки для определения того, нужно менять местами два соседних элемента или нет. Всего у функции возможны три разных варианта возврата: + +```ruby +1 <=> 1 # 0 числа равны +2 <=> 1 # 1 левое больше правого +1 <=> 2 # -1 левое меньше правого +``` + +Вашей задачей в этом задании будет написать функцию `compare()`, которая ведёт себя так же, как и spaceship-оператор: + +```ruby +compare(1, 1) # 0 числа равны +compare(2, 1) # 1 левое больше правого +compare(1, 2) # -1 левое меньше правого +``` diff --git a/modules/10-basics/30-if/ru/README.md b/modules/10-basics/30-if/ru/README.md new file mode 100644 index 0000000..92d740e --- /dev/null +++ b/modules/10-basics/30-if/ru/README.md @@ -0,0 +1,23 @@ + +Условия в Ruby выглядят похожими на условия в других языках: + +```ruby +def sentence_type(text) + # проверяем последний символ в строке + if text.end_with?('?') + 'question' # снова нет return + elsif text.end_with?('!') + 'exclamation' # и тут + else + 'statement' # и тут + end +end + +sentence_type 'anybody here?' # question +sentence_type 'Hexlet for humans' # statement +sentence_type 'boo!' # exclamation +``` + +В Ruby условиям не нужны скобки. Выражения пишутся сразу после ключевого слова `if`. Для дополнительных условий используется ключевое словое `elsif`. И для всего остального — привычный `else`. В конце добавляется `end`. + +В примере используется предикат (функция, возвращающая `true` или `false`) `end_with?()`. Ключевое отличие Ruby от других популярных языков — в использовании знака вопроса на конце, вместо `is` и его аналогов в начале. Этот подход перекочевал из Lisp-языков. diff --git a/modules/10-basics/30-if/ru/data.yml b/modules/10-basics/30-if/ru/data.yml new file mode 100644 index 0000000..3edba42 --- /dev/null +++ b/modules/10-basics/30-if/ru/data.yml @@ -0,0 +1,2 @@ +name: Условные конструкции +tips: [] diff --git a/modules/10-basics/33-if-extra/ru/EXERCISE.md b/modules/10-basics/33-if-extra/ru/EXERCISE.md new file mode 100644 index 0000000..6dbe64d --- /dev/null +++ b/modules/10-basics/33-if-extra/ru/EXERCISE.md @@ -0,0 +1,16 @@ + +Реализуйте функцию `get_sentence_tone()`, которая принимает строку и определяет тон предложения. Если все символы в верхнем регистре, то это вопль — `'scream'`. В ином случае — нормальное предложение — `'general'`. + +Примеры вызова: + +```ruby +get_sentence_tone('Hello') # general +get_sentence_tone('WOW') # scream +``` + +Алгоритм: + +1. Сгенерируйте строку в верхнем регистре на основе строки-аргумента с помощью метода `upcase`. +2. Сравните её с исходной строкой: + - Если строки равны, значит строка-аргумент в верхнем регистре. + - В ином случае — строка-аргумент не в верхнем регистре. diff --git a/modules/10-basics/33-if-extra/ru/README.md b/modules/10-basics/33-if-extra/ru/README.md new file mode 100644 index 0000000..dcb4e17 --- /dev/null +++ b/modules/10-basics/33-if-extra/ru/README.md @@ -0,0 +1,40 @@ + +Ruby поддерживает множество видов условных конструкций, которые иногда способны сделать код чуть проще и прямолинейнее. Все они встречаются в реальном коде регулярно. + +## Тернарный оператор + +Работает и выглядит аналогично другим языкам: + +```ruby +# ? : +v = 3 == 4 ? 1 : 0 +``` + +## Постфиксный if + +В Ruby *if* может стоять не только в начале, но и в конце выражений: + +```ruby +do_something() if num.zero? +``` + +Подобную форму записи принято использовать тогда, когда все выражение помещается в одну строчку. + +## Unless + +В дополнение к *if*, в Ruby есть конструкция *unless*, которая работает в обратную сторону: + +```ruby +# Пока (если) something не zero? +unless something.zero? + # что-то делаем +end +``` + +*unless* позволяет избавляться от отрицаний, но с ним нужно быть осторожным. Если в предикате используется составное логическое выражение, то *unless* становится не читаемым: + +```ruby +# Попробуйте осознать этот код +unless a && b +end +``` diff --git a/modules/10-basics/33-if-extra/ru/data.yml b/modules/10-basics/33-if-extra/ru/data.yml new file mode 100644 index 0000000..874480c --- /dev/null +++ b/modules/10-basics/33-if-extra/ru/data.yml @@ -0,0 +1,2 @@ +name: Условные конструкции (альтернативные варианты) +tips: [] diff --git a/modules/10-basics/35-strings/ru/EXERCISE.md b/modules/10-basics/35-strings/ru/EXERCISE.md new file mode 100644 index 0000000..4c7a8f6 --- /dev/null +++ b/modules/10-basics/35-strings/ru/EXERCISE.md @@ -0,0 +1,9 @@ + +Номер банковской карточки является персональной и достаточно чувствительной информацией. Поэтому никакие системы не показывают его полностью даже владельцу карты (вдруг кто-то украл доступ в аккаунт). Вместо этого в интерфейсах часто показывают четыре последних символа, а остальное скрывают за зведочками. + +Реализуйте функцию `prepare_card_for_display()`, которая принимает на вход номер карты (в виде строки) и возвращает строку, в которой шесть символов. Первые два — звездочки, последние четыре соответствуют последним четырём цифрам в номере карты: + +```ruby +prepare_card_for_display('1234789023457890') # '**7890' +prepare_card_for_display('0192837409128735') # '**8735' +``` diff --git a/modules/10-basics/35-strings/ru/README.md b/modules/10-basics/35-strings/ru/README.md new file mode 100644 index 0000000..4e743d9 --- /dev/null +++ b/modules/10-basics/35-strings/ru/README.md @@ -0,0 +1,50 @@ + +Базовые операции со строками в любом языке – это интерполяция и конкатенация. Как это делается на Ruby: + +```ruby +# Конкатенация +name = 'H' + 'e' + 'x' + 'l' + 'e' + 't' # Hexlet +# Можно даже так +name = 'H' 'e' 'x' 'l' 'e' 't' # Hexlet +# Интерполяция +greeting = "hello #{name}" # hello Hexlet +``` + +Интерполяция выполняется только в строках с двойными кавычками. Строки, состоящие из одиночных кавычек, воспринимаются «как есть» — по аналогии с PHP. + +```ruby +line_feed = "one line\nanother one" +# one line +# another one +``` + +Как и в большинстве других языков, интерполяция в Ruby предпочтительнее конкатенации. + +Помимо стандартных конкатенации и интерполяции, строки в Ruby усилены срезами и большим числом полезных методов. Вот лишь некоторые примеры: + +```ruby +name = 'ruby' +name.upcase # 'RUBY' +''.empty? # true +name.include? 'ru' # true +name.capitalize # Ruby +name.length # 4 +``` + +Одна из самых классных возможностей у строк – слайсы. С их помощью можно находить любую подстроку в строке. Слайс — тоже метод, но с дополнительным синтаксисом из квадратных скобок: + +```ruby +name = 'ruby' +# второй символ (счёт идёт с нуля) +name[1] # u +# Последний символ +name[-1] # y +# два символа, начиная с нулевого индекса +name[0, 2] # ru +# от первого до второго с конца +name[0..-2] # rub +# от 4 с конца до последнего символа +name[-4..] # ruby +# и даже регулярные выражения! +name[/ru/] # ru +``` diff --git a/modules/10-basics/35-strings/ru/data.yml b/modules/10-basics/35-strings/ru/data.yml new file mode 100644 index 0000000..f7d550d --- /dev/null +++ b/modules/10-basics/35-strings/ru/data.yml @@ -0,0 +1,7 @@ +name: Строки +tips: + - > + [Способы определения + строк](https://ruby-doc.org/core-3.0.0/doc/syntax/literals_rdoc.html#label-Strings) + - | + [Методы строк](https://ruby-doc.org/core-3.0.0/String.html) diff --git a/modules/10-basics/40-blocks/ru/EXERCISE.md b/modules/10-basics/40-blocks/ru/EXERCISE.md new file mode 100644 index 0000000..7ce9f90 --- /dev/null +++ b/modules/10-basics/40-blocks/ru/EXERCISE.md @@ -0,0 +1,9 @@ + +Реализуйте функцию `show_me_numbers()`, которая выводит на экран числа от одного до переданного в функцию в обратном порядке: + +```ruby +show_me_numbers(3) +# => 3 +# => 2 +# => 1 +``` diff --git a/modules/10-basics/40-blocks/ru/README.md b/modules/10-basics/40-blocks/ru/README.md new file mode 100644 index 0000000..a6c1a35 --- /dev/null +++ b/modules/10-basics/40-blocks/ru/README.md @@ -0,0 +1,112 @@ + +Блоки в Ruby — очень важная концепция, которая встречается на каждом шагу. У неё нет аналогов в популярных языках, поэтому при изучении блоков сложно опираться на прошлый опыт. К счастью, их не так сложно понять, особенно если у вас есть опыт работы с лямбда-функциями (анонимными функциями). Начнём с примера: + +```ruby +# Переводится как «пять раз». В этом весь Ruby. +5.times do |i| + puts i +end +# => 0 +# => 1 +# => 2 +# => 3 +# => 4 +``` + +Всего три строчки, но очень много новых смыслов. Если говорить в общем, то здесь вызывается метод `times()`, который принимает на вход блок кода и вызывает его пять раз. + +Блок кода — это конструкция *do end*. Блок очень похож на функцию, которая передается в функцию `times()`. Но передается довольно необычным способом. Ключевое слово *do* начинается после того, как закрыты вызывающие скобки у метода. Блок просто отделяется пробелом от вызова самой функции. + +```ruby +# После пятёрки нет запятой! +5.times() do |i| + puts i +end + +# А так не сработает +5.times(do |i| + puts i +end) +``` + +Как это работает? Блоки в Ruby обычно передаются в функции, как особый аргумент, который идёт вне вызова функции, что видно по примеру сверху. Внутреннюю работу блоков в функциях мы рассмотрим позже, когда немного научимся использовать блоки. + +Это довольно необычная концепция. Сама по себе она не привносит никаких новых возможностей в язык, но даёт новые визуальные возможности по оформлению кода. Именно из-за этой особенности Ruby так хорошо подходит и часто используется, как язык для построения DSL (языков предметной области). Подробнее об этом в следующих уроках. + +И, наконец, сам блок. Можно представить, что внутри функции он попадает в переменную, которая вызывается, как обычная функция. Сам блок — как функция (а он является в том числе функцией), и умеет принимать параметры. Внутрь блока они попадают через конструкцию `|i|`, идущую сразу после *do*. Этот синтаксис пришел в Ruby из Smalltalk. Если параметров несколько, то они просто перечисляются через запятую `|one, two|`. + +Блок работает как замыкание, а значит внутри него можно использовать любые переменные, определенные снаружи и выше блока: + +```ruby +name = 'ruby' +3.times do # а параметры блока можно опускать + puts name +end +# => ruby +# => ruby +# => ruby +``` + +У блоков есть альтернативный синтаксис. Пример выше можно было записать так: + +```ruby +5.times { |i| puts i } +``` + +Подобную запись используют в том случае, когда содержимое блока помещается на одну строку. Синтаксис `do/end` никогда не используют для однострочных блоков. + +Если быть до конца честными, то эти два синтаксиса работают немного по-разному. У `{}` приоритет выше, чем у `do/end`. Это важно, когда идёт вложенный вызов нескольких функций, и каждая из них умеет работать с блоками. Давайте разберём пример: + +```ruby +# Обе функции вызываются и печатают на экран приветствие, если им передан блок +# Подставьте в них мысленно скобки +print_hello_f1 { "Dima" } +# => "Hello from f1, Dima" +print_hello_f2 { "Vasya" } +# => "Hello from f2, Vasya" + +# При таком вызове функции ничего не печатают, так как нет блока +print_hello_f1 +print_hello_f2 + +print_hello_f1 print_hello_f2 { "Dima" } +# => ? + +print_hello_f1 print_hello_f2 do + "Vasya" +end +# => ? +``` + +Однострочный вариант блока будет относиться к самой правой функции. При полной форме `do ... end` блок относится к самой первой функции + +```ruby +print_hello_f1 print_hello_f2 print_hello_f3 { 'Petya' } +# => "Hello from f3, Petya" +print_hello_f1 print_hello_f2 print_hello_f3 do 'Vasya' end +# => "Hello from f1, Vasya" +``` + +С помощью скобок можно определить, к какой функции блок будет относиться: + +```ruby +# Равнозначные варианты. Скобки определяют, куда будет отнесён блок +# f1(f2()) do ... end +print_hello_f1(print_hello_f2()) { 'Petya' } +# => "Hello from f1, Petya" +print_hello_f1 print_hello_f2 do + 'Petya' +end +# => "Hello from f1, Petya" + +# Равнозначные варианты +# f1(f2() do ... end) +print_hello_f1(print_hello_f2() { 'Petya' }) +# => "Hello from f2, Petya" +print_hello_f1(print_hello_f2 do + 'Petya' +end) +# => "Hello from f2, Petya" +``` + +Не переживайте, если прямо сейчас блоки вам непонятны. Для их осознания нужно время и практика. В Ruby они встречаются повсеместно, поэтому понимание работы с блоками приходит быстро. Буквально в следующем модуле они будут уже везде. diff --git a/modules/10-basics/40-blocks/ru/data.yml b/modules/10-basics/40-blocks/ru/data.yml new file mode 100644 index 0000000..ee99d81 --- /dev/null +++ b/modules/10-basics/40-blocks/ru/data.yml @@ -0,0 +1,2 @@ +name: Блоки +tips: [] diff --git a/modules/10-basics/45-iteration/ru/EXERCISE.md b/modules/10-basics/45-iteration/ru/EXERCISE.md new file mode 100644 index 0000000..61c9ea2 --- /dev/null +++ b/modules/10-basics/45-iteration/ru/EXERCISE.md @@ -0,0 +1,7 @@ + +Напишите функцию `average()`, считающую среднее арифметическое для всех чисел в диапазоне. Функция должна возвращать Float: + +```ruby +average(5, 8) # (5 + 6 + 7 + 8) / 4; 26 / 4; 6.5 +average(2, 4) # (2 + 3 + 4) / 3; 9 / 3; 3.0 +``` diff --git a/modules/10-basics/45-iteration/ru/README.md b/modules/10-basics/45-iteration/ru/README.md new file mode 100644 index 0000000..55f410a --- /dev/null +++ b/modules/10-basics/45-iteration/ru/README.md @@ -0,0 +1,32 @@ + +`times()` — простой метод, но слишком ограниченный. Что, например, делать, если нужно обходить какой-то конкретный диапазон чисел в прямом и обратном порядке? Для этого в Ruby есть методы `upto()` и `downto()`: + +```ruby +5.upto(7) do |i| + puts i +end +# => 5 +# => 6 +# => 7 +``` + +Этот метод «поднимается» от цифры, на которой был вызван, до переданного числа. Исходное число и конечное включаются в обход. Вот как, например, посчитать сумму этих чисел: + +```ruby +result = 0 +5.upto(7) do |i| + result += i +end +puts result # => 18 +``` + +Так как блок по своей сути — функция, то на него распространяются правила области видимости функций. Все, что определено внутри блока, недоступно снаружи, но блок может «замкнуть» внутри себя внешние переменные, как в примере выше. Переменные замыкаются по ссылке — это значит, что изменение переменной внутри меняет её и снаружи. + +Обратите внимание на вызов метода `upto()` и передачу блока. Блок передается внутрь метода, но происходит это вне вызывающих скобок. Пример выше можно переписать так: + +```ruby +5.upto 7 do |i| +... +``` + +Для тех, кто не привык к Ruby, такой синтаксис может показаться странным, но это быстро проходит. В какой-то момент вы почувствуете, что вас начнет раздражать ставить скобки в других языках) diff --git a/modules/10-basics/45-iteration/ru/data.yml b/modules/10-basics/45-iteration/ru/data.yml new file mode 100644 index 0000000..5e0ca35 --- /dev/null +++ b/modules/10-basics/45-iteration/ru/data.yml @@ -0,0 +1,2 @@ +name: Итераторы +tips: [] diff --git a/modules/10-basics/47-iteration-over-string/ru/EXERCISE.md b/modules/10-basics/47-iteration-over-string/ru/EXERCISE.md new file mode 100644 index 0000000..9f5fe4f --- /dev/null +++ b/modules/10-basics/47-iteration-over-string/ru/EXERCISE.md @@ -0,0 +1,7 @@ + +Реализуйте функцию `reverse()`, которая переворачивает строку. Не используйте в своем решении метод строки reverse + +```ruby +reverse 'hexlet' # telxeh +reverse 'basics' # scisab +``` diff --git a/modules/10-basics/47-iteration-over-string/ru/README.md b/modules/10-basics/47-iteration-over-string/ru/README.md new file mode 100644 index 0000000..ba2a798 --- /dev/null +++ b/modules/10-basics/47-iteration-over-string/ru/README.md @@ -0,0 +1,34 @@ + +Для закрепления блоков рассмотрим ещё один пример итерации – обход строки. В большинстве языков для обхода строки есть два варианта: + +* Преобразование строки в массив символов и обход этого массива +* Проход по числам от нуля до длины строки - 1. Обращение к символам по индексу. + +В таких моментах проявляется выразительность Ruby и мощь его встроенной библиотеки. Для обхода строки ничего не нужно придумывать, всё уже встроено: + +```ruby +company_name = 'hexlet' +company_name.each_char do |c| # c – значит char + puts c +end +# => h +# => e +# => x +# => l +# => e +# => t +``` + +Метод [each_char()](https://ruby-doc.org/core-2.7.2/String.html#method-i-each_char) определен именно на строке. + +Ниже чуть более сложный пример с агрегацией. Код, который считает количество букв *e* в строке: + +```ruby +company_name = 'hexlet' +letters_count = 0 +company_name.each_char do |c| + if c == 'e' + letters_count += 1 + end +end +``` diff --git a/modules/10-basics/47-iteration-over-string/ru/data.yml b/modules/10-basics/47-iteration-over-string/ru/data.yml new file mode 100644 index 0000000..b614e03 --- /dev/null +++ b/modules/10-basics/47-iteration-over-string/ru/data.yml @@ -0,0 +1,2 @@ +name: Обход строки +tips: [] diff --git a/modules/10-basics/50-datatypes/ru/EXERCISE.md b/modules/10-basics/50-datatypes/ru/EXERCISE.md new file mode 100644 index 0000000..b1c088e --- /dev/null +++ b/modules/10-basics/50-datatypes/ru/EXERCISE.md @@ -0,0 +1,11 @@ + +Реализуйте функцию `type_of_type(value)`, которая определяет, является ли тип данных составным, и возвращает символ `:complex`, если да, и `:simple` — если нет. Составным считаются только три типа: `Hash`, `Array` и `Range`. Остальные — простые. + +```ruby +type_of_type({}) # :complex +type_of_type('') # :simple +``` + +Для проверки условия *ИЛИ* используйте оператор `||`. + +Не забудьте при этом поставить скобки вокруг вызова методов: `1.is_a?(String)`. Иначе код выполнится не так, как задумано, из-за приоритетов операций. diff --git a/modules/10-basics/50-datatypes/ru/README.md b/modules/10-basics/50-datatypes/ru/README.md new file mode 100644 index 0000000..63862be --- /dev/null +++ b/modules/10-basics/50-datatypes/ru/README.md @@ -0,0 +1,61 @@ + +Большая часть типов данных в Ruby работает аналогично типам в других языках. Это относится к строкам, числам или булевым значениям: + +```ruby +'one' # String +true # Boolean +false +3 # Integer +5.2 # Float +[] # Array +{} # Hash +1..3 # Range +``` + +Роль `null` в ruby играет `nil`. Такое название традиционно используется в Smalltalk и семействе Lisp-языков для обозначения концепции «ничего». В остальном всё очень похоже. + +Тип Range используется для задания непрерывного диапазона чисел. Встречается не так часто, но бывает удобен, особенно при работе с датами в библиотеках. + +```ruby +# От сейчас до +24 часа +Time.now..(Time.now + 60 * 60 * 24) +``` + +Массивы в Ruby, как и в любом другом динамическом языке, тоже динамические. Их можно изменять, уменьшать и увеличивать прямо в процессе работы: + +```ruby +companies = [] +# Добавление элемента +companies << 'hexlet' +``` + +Тип данных Hash хотя и называется необычно, по сути является обычным хранилищем key-value пар. В JavaScript для этого используются объекты, в PHP — ассоциативные массивы, а в Python — словари. + +```ruby +options = { + sanitize: true +} +options['table'] = false +``` + +Кроме стандартных типов Ruby добавляет довольно необычный тип, называемый символом. Подобный тип есть во многих языках, включая Erlang (атом), Clojure (кейворд) и JavaScript (символ). Пример: + +```ruby +:key +:anotherone +``` + +Символ очень похож на строку, но он представлен в памяти в единственном экземпляре — в отличие от строк, которые создаются каждый раз заново. Это если говорить про техническую сторону. На практике символ удобен там, где строка выражает саму себя. Обычно там, где в других языках создают константы, в которые записаны строки с именем этой константы, в Ruby просто используют символ. По большому счёту символ не является чем-то жизненно необходимым — он немного сокращает количество кода и местами чуть удобнее в использовании. В остальном по смыслу — это та же строка. + +Символы — это такая концепция, которая «заходит» исключительно на практике. Чем больше примеров будет перед глазами, тем быстрее получится использовать этот тип по назначению. + +Для проверки типа в каждом объекте присутствует метод `is_a?`: + +```ruby +1.is_a? String # false +1.is_a? Integer # true + +# Либо сам тип +1.class # Integer +'hexlet'.class # String +``` diff --git a/modules/10-basics/50-datatypes/ru/data.yml b/modules/10-basics/50-datatypes/ru/data.yml new file mode 100644 index 0000000..e8e42dd --- /dev/null +++ b/modules/10-basics/50-datatypes/ru/data.yml @@ -0,0 +1,2 @@ +name: Типы данных Ruby +tips: [] diff --git a/modules/10-basics/60-classes/ru/EXERCISE.md b/modules/10-basics/60-classes/ru/EXERCISE.md new file mode 100644 index 0000000..33b6ae7 --- /dev/null +++ b/modules/10-basics/60-classes/ru/EXERCISE.md @@ -0,0 +1,16 @@ + +Реализуйте функцию `next_day()`, которая возвращает дату начала следующего дня (12:00 a.m.). Функция должна возвращать объект типа Time. + +```ruby +# Дата вызова: 16.12.2020 +next_day(); # 2020-12-17 00:00:00 +0000 +``` + +Эту задачу можно реализовать разными способами. Проще всего создать сегодняшнюю дату (`Date.today`), а потом на основе данных из этого объекта создать объект времени. + +```ruby +today = Date.today +today.day # день +today.month # месяц +today.year # год +``` diff --git a/modules/10-basics/60-classes/ru/README.md b/modules/10-basics/60-classes/ru/README.md new file mode 100644 index 0000000..6e8be21 --- /dev/null +++ b/modules/10-basics/60-classes/ru/README.md @@ -0,0 +1,68 @@ + +ООП — отдельная большая тема в Ruby с миллионом своих особенностей. Сейчас мы ещё не готовы её рассматривать плотно, но как минимум нужно знать некоторые базовые конструкции, которые используются широко. В первую очередь речь про классы. Посмотрите на создание даты в Ruby: + +```ruby +# Чтобы использовать класс даты, его необходимо подключить +require 'date' + +# Именование классов в Ruby соответствует стилю CamelCase, +# что отличает их от функций/методов и переменных +# Ниже вызовы методов + +d = Date.today +d.next # возвращает объект следующего дня + +# Создаёт дату на сегодняшний день и текущее время +time = Time.new +time.thursday? # false +``` + +Time — это класс. А сама запись означает: вызвать метод `new()` у класса. `new` в Ruby — не отдельный оператор, а обычный метод. Конечно, не совсем обычный, но синтаксически он ничем не отличается от обычных методов. Такой синтаксис появился не просто так, ниже вы поймете почему. При этом он позволяет проще делать цепочки «по месту»: `Time.new.to_i` (возвращает unixtimestamp). Ещё немного примеров + +```ruby +# Создаёт дату на 00:00 10-12-2020 +Time.new 2020, 12, 10 +``` + +Как мы помним, конструктор принадлежит не объектам, а самому классу, поэтому он вызывается на классе. Но раз это метод, то что же такое класс? Посмотрите ещё на пару примеров: + +```ruby +# now, а не new +time1 = Time.now +time2 = Time.utc # возвращает объект времени в utc +``` + +Здесь, в зависимости от вашего бэкграунда, ответы могут отличаться друг от друга. Кто-то из PHP скажет, что `now` — статический метод, кто-то скажет, что может быть классов нет, а есть только объекты, как в JavaScript. В Ruby нет статических свойств и методов, и есть реальные классы. Но классы — это объекты, у которых есть свои методы и ... классы. + +```ruby +Time.methods.count # 126 +Time.class # класс Time — это Class +Time.class.class # а у класса Class класс — это Class :D +Time.superclass # а у класса есть и родитель Object +Time.superclass.class # у которого класс — это Class +Time.superclass.superclass # а родитель родителя — это BasicObject +# и это мы ещё не говорим про классы объектов +# А ещё можно создать класс +Class.new ... # так как класс — это не только объект, но и класс +``` + +То есть фразу в Ruby «все есть объект» надо понимать практически буквально, но это сильно отличается от того же JavaScript, где под объектом понимается конкретный тип данных object. Объектная модель Ruby наиболее близка языку SmallTalk. С первого взгляда она пугает, но со временем к ней не просто привыкаешь, она становится интуитивной. + +Что с этим совсем делать? Пока ничего. Знание того, как работать с классами и как их конструировать, понадобится при взаимодействии с фреймворками и библиотеками. Эта тема рассматривается в самом конце курса, а пока небольшой пример из Rails: + +```ruby +# Просто демонстрация на подумать, понимать этот код не нужно +class User < ApplicationRecord # < – наследование + # Это вызов метода прямо в определении класса + validates :email, presence: true, uniqueness: true + + # И это вызовы методов + has_many :companies + belongs_to :company + + def to_s + # this/self не нужны + "#{first_name} #{last_name}" + end +end +``` diff --git a/modules/10-basics/60-classes/ru/data.yml b/modules/10-basics/60-classes/ru/data.yml new file mode 100644 index 0000000..91399bf --- /dev/null +++ b/modules/10-basics/60-classes/ru/data.yml @@ -0,0 +1,5 @@ +name: Классы +tips: + - >- + Классы в Ruby загружаются автоматически через загрузчик. Их не подключают + явно, как в JavaScript. diff --git a/modules/10-basics/65-modules/ru/EXERCISE.md b/modules/10-basics/65-modules/ru/EXERCISE.md new file mode 100644 index 0000000..c98838b --- /dev/null +++ b/modules/10-basics/65-modules/ru/EXERCISE.md @@ -0,0 +1,15 @@ + +Создайте модуль `MyNumber`. Реализуйте внутри модуля функцию `reverse_int()`, которая переворачивает цифры в переданном числе (типа Integer) и возвращает новое число. + +```ruby +reverse_int(13) # 31 +reverse_int(-123) # -321 +reverse_int(8900) # 98 +``` + +Полезные методы: + +* `abs()` – возвращает модуль числа +* `to_s()` – преобразование в строку +* `to_i()` – преобразование в число +* `reverse()` – переворачивает строку diff --git a/modules/10-basics/65-modules/ru/README.md b/modules/10-basics/65-modules/ru/README.md new file mode 100644 index 0000000..72e3b10 --- /dev/null +++ b/modules/10-basics/65-modules/ru/README.md @@ -0,0 +1,47 @@ + +Одна из главных концепций в любом языке – разбиение кода на независимые модули с локальным именованием. В разных языках это делается разными словами, но похожими механизмами, такими как неймспейсы, пакеты, модули. + +В Ruby для этого используется система модулей. Функции, определенные внутри модуля, локальны относительно этого модуля. То есть разные модули могут иметь внутри функции с одинаковыми именами. + +```ruby +# file: module_name.rb +module MyModule + def self.run + 'go!' + end +end +``` + +При определении функции внутри модуля, к ней нужно добавлять префикс `self.`, только в этом случае получится её вызвать напрямую из модуля. Без `self.` функции тоже объявляют, но только тогда, когда модуль играет роль миксина. Это очень интересная концепция, которая всё чаще встречается в других языках. Подробнее о ней в соответствующем разделе. + +```ruby +MyModule.run # 'go!' +``` + +Вызов функции из модуля выглядит абсолютно идентично вызову методов из класса. Является ли модуль классом? Это легко проверить: + +```ruby +MyModule.new # undefined method `new' for MyModule:Module +# При этом модуль это объект +MyModule.class # Module +``` + +Как понять что перед нами класс, а не модуль? Короткий ответ: никак. Обычно это не проблема, потому что из контекста понятно, с чем идёт работа. В Ruby-мире много соглашений по тому, как работают библиотеки и как организуется код. Благодаря этому резко упрощается понимание происходящего. + +Встроенных в Ruby модулей не так много. Из наиболее простого можно назвать Math, в котором лежат разные полезные математические функции: + +```ruby +# Можно обращаться напрямую, +# встроенные модули всегда загружены +Math.sin(1) +``` + +Из наиболее используемых — модуль Kernel. С его помощью управляют поведением процесса и интерпретатора. + +```ruby +Kernel.exit +``` + +Особенность этого модуля в том, что все функции модуля Kernel доступны для вызова напрямую, без указания имени модуля. Это происходит за счёт того, что модуль Kernel "подмешан" в класс Object, от которого наследуются практически все классы в Руби (неявно). + +Если смотреть дальше, то возникает множество вопросов и разных интересных ситуаций: вложенные модули, модули вложенные в классы, классы вложенные в модули и даже классы вложенные в классы. Все это возможно в Ruby. А как соотносятся файловая система и модули? А можно ли определять классы без модулей? А можно ли вызывать функции модуля из функций модуля? К сожалению, не представляется возможным разобрать все эти вопросы в рамках нашего материала. За дальнейшими подробностями добро пожаловать на Хекслет! diff --git a/modules/10-basics/65-modules/ru/data.yml b/modules/10-basics/65-modules/ru/data.yml new file mode 100644 index 0000000..64d54a5 --- /dev/null +++ b/modules/10-basics/65-modules/ru/data.yml @@ -0,0 +1,2 @@ +name: Модули +tips: [] diff --git a/modules/10-basics/67-everything-is-expression/ru/EXERCISE.md b/modules/10-basics/67-everything-is-expression/ru/EXERCISE.md new file mode 100644 index 0000000..36322ff --- /dev/null +++ b/modules/10-basics/67-everything-is-expression/ru/EXERCISE.md @@ -0,0 +1,13 @@ + +Реализуйте функцию `invert_case()`, которая меняет в строке регистр каждой буквы на противоположный. + +```ruby +invert_case('Hello, World!') # hELLO, wORLD! +invert_case('I loVe JS') # i LOvE js +``` + +Полезные методы: + +* `upcase()` – перевод строки в верхний регистр +* `downcase()` – перевод строки в нижний регистр +* [each_char()](https://ruby-doc.org/core-2.7.2/String.html#method-i-each_char) – обход строки побуквенно diff --git a/modules/10-basics/67-everything-is-expression/ru/README.md b/modules/10-basics/67-everything-is-expression/ru/README.md new file mode 100644 index 0000000..1c27065 --- /dev/null +++ b/modules/10-basics/67-everything-is-expression/ru/README.md @@ -0,0 +1,25 @@ + +Кроме того, что в Ruby всё есть объект, практически всё — ещё и выражение. Что это значит? Возьмём для примера условные конструкции, например, `if`. `if` в большинстве языков — это конструкция языка, которая может появляться только в определенных местах. Её нельзя использовать в составе каких-то выражений. В Ruby это не так: + +```ruby +# Так как if выражение, значит +# мы можем присваивать результат его выполнения +a = if 5 > 3 + 'yes' +else + 'no' +end + +puts a # => 'yes' +``` + +То же самое касается любых других конструкций, вплоть до определения функций/методов, модулей, классов: + +```ruby +# В f попадает не сама функция, а её имя в виде символа +f = def foo +end +puts f # :foo +``` + +Зачем это нужно? Чем больше языковых конструкций реализовано в виде выражений, тем более **выразительным** является язык. В таком языке больше возможностей за счёт комбинирования конструкций, как в примере с `if`. diff --git a/modules/10-basics/67-everything-is-expression/ru/data.yml b/modules/10-basics/67-everything-is-expression/ru/data.yml new file mode 100644 index 0000000..e56fb30 --- /dev/null +++ b/modules/10-basics/67-everything-is-expression/ru/data.yml @@ -0,0 +1,2 @@ +name: Всё есть выражение +tips: [] diff --git a/modules/10-basics/70-summary/ru/EXERCISE.md b/modules/10-basics/70-summary/ru/EXERCISE.md new file mode 100644 index 0000000..38e73cc --- /dev/null +++ b/modules/10-basics/70-summary/ru/EXERCISE.md @@ -0,0 +1,18 @@ + +В мире существует множество разных билетов: на транспорт, в кино, в театр и так далее. И, как правило, у каждого билета есть свой индивидуальный номер. Считается, что билет счастливый, если сумма цифр, входящих в этот билет с самого начала и до середины, равна сумме цифр после середины этого числа. Например, если номер билета *723428*, то нам нужно сложить *7 + 2 + 3* и сравнить это с *4 + 2 + 8*. Данный билет счастливым не является. + +Напишите функцию `happy?()`, которая определяет, счастливый ли перед нами билет. Если билет счастливый, функция должна вернуть `true`, если нет — `false`. Функция должна работать только с числом, состоящим из чётного количества цифр: + +```ruby +happy?(77) # true +happy?(7881) # false +happy?(732930) # true +``` + +Алгоритм функции достаточно простой. Нужно превратить число в строку, поделить пополам (можно взять подстроку), и пройтись по каждой половине, считая сумму цифр. Для подсчета лучше написать отдельную функцию, чтобы не дублировать эту логику. + +Для решения этой задачи вам могут понадобится: + +* `to_s()` – метод конвертации в строку +* `to_i()` – метод конвертации в число +* [each_char()](https://ruby-doc.org/core-2.7.2/String.html#method-i-each_char) – обход строки побуквенно diff --git a/modules/10-basics/70-summary/ru/README.md b/modules/10-basics/70-summary/ru/README.md new file mode 100644 index 0000000..38b919f --- /dev/null +++ b/modules/10-basics/70-summary/ru/README.md @@ -0,0 +1,27 @@ + +Ruby — мощный скриптовый язык программирования, который создавался с целью сделать программирование максимально «человечным». В этом языке очень много идиоматики, то есть принципов, как делать те или иные штуки: правильно и в стиле Ruby Way. Это очень похоже на Python с его Pythonic, но Ruby Way местами идёт в противоположную сторону. В этом месте программисты делятся на два лагеря. Одним Ruby Way приходится по душе, другие же его не принимают. + +Ruby создавался таким, чтобы одну и ту же задачу можно было сделать большим количеством способов. Например, у многих методов существуют алиасы, которые не добавляют новой функциональности — они просто существуют для логичности (стройности) языка. Пример из Rails: `1.day + 5.days`. У обоих операндов вызывается один и тот же метод, но у него разные названия — для того, чтобы можно было легко прочесть написанное. + +Ruby, благодаря лаконичному синтаксису с минимумом шума и наличию блоков, позволяет создавать DSL буквально под каждую задачу, чем программисты с удовольствием пользуются. Это одна из причин, почему язык стал популярен в DevOps-среде, где было создано множество инструментов, написанных на Ruby (Chef, Puppet и другие). + +```ruby +# Вроде бы просто роутинг, но он не выглядит как код +Rails.application.routes.draw do + root to: "pages#main" + + resources :posts do + get :preview + + resources :comments, only: [:new, :create, :destroy] + end +end +``` + +Подводя итог, можно сказать, что Ruby очень приятный и удобный для разработки язык. Попробуйте взять код ниже и написать на вашем текущем языке, а затем сравните: + +```ruby +# Что делает этот код? +sentence = ' Ruby one love ' +sentence.strip.split.last.size # 4 +``` diff --git a/modules/10-basics/70-summary/ru/data.yml b/modules/10-basics/70-summary/ru/data.yml new file mode 100644 index 0000000..03d0614 --- /dev/null +++ b/modules/10-basics/70-summary/ru/data.yml @@ -0,0 +1,2 @@ +name: Особенности языка +tips: [] diff --git a/modules/20-collections/10-arrays-basics/ru/EXERCISE.md b/modules/20-collections/10-arrays-basics/ru/EXERCISE.md new file mode 100644 index 0000000..709fb46 --- /dev/null +++ b/modules/20-collections/10-arrays-basics/ru/EXERCISE.md @@ -0,0 +1,7 @@ + +Напишите функцию `intersection()`, которая находит пересечение двух массивов. Под пересечением понимается новый массив, который состоит из элементов, входящих одновременно в оба исходных массива: + +```ruby +intersection([1, 3, 8], [9, 3, 1]); # [1, 3] +intersection([8], [5, 0]) # [] +``` diff --git a/modules/20-collections/10-arrays-basics/ru/README.md b/modules/20-collections/10-arrays-basics/ru/README.md new file mode 100644 index 0000000..bb8ccba --- /dev/null +++ b/modules/20-collections/10-arrays-basics/ru/README.md @@ -0,0 +1,48 @@ + +Массивы в Ruby работают так же, как и массивы в любых других динамических языках. Отличия в основном в некоторых синтаксических конструкциях и в большом количестве встроенных методов. Основные операции: + +```ruby +items = [1, 2] + +# Добавление в конец +items << 3 # [1, 2, 3] +# Добавление в начало +items.unshift 5 # [5, 1, 2, 3] + +items.first # 5 +# Последний элемент +items.last # 3 + +items.length # 4 +# не пустой? +items.any? # true +# пустой? +items.empty? # false + +# входит ли value в items +items.include? value + +# Обращение к несуществующему элементу возвращает nil +items[5] # nil + +items.sum # 11 +``` + +Массивы в Ruby передаются по ссылке, как в Python и JavaScript. Но в отличие от JavaScript сравнение массивов идёт по значениям: + +```ruby +items = [1, 2] +items == [1, 2] # true +``` + +Для обхода массивов в Ruby используется метод `each()`. По принципу работы он похож на `times()`, но применяется к коллекциям. `each()` принимает блок, который вызывается для каждого элемента коллекции. Внутрь блока передаётся текущий элемент. + +```ruby +# Сбор в строчку +items = [1, 2, 3] +result = '' +items.each do |item| + result = "#{result}#{item}" +end +puts result # => '123' +``` diff --git a/modules/20-collections/10-arrays-basics/ru/data.yml b/modules/20-collections/10-arrays-basics/ru/data.yml new file mode 100644 index 0000000..7348d79 --- /dev/null +++ b/modules/20-collections/10-arrays-basics/ru/data.yml @@ -0,0 +1,4 @@ +name: Массивы +tips: + - | + [Методы массивов](https://ruby-doc.org/core-3.0.0/Enumerator.html) diff --git a/modules/20-collections/20-arrays-methods/ru/EXERCISE.md b/modules/20-collections/20-arrays-methods/ru/EXERCISE.md new file mode 100644 index 0000000..d89ebdc --- /dev/null +++ b/modules/20-collections/20-arrays-methods/ru/EXERCISE.md @@ -0,0 +1,32 @@ + +Некая организация занимается аналитикой распространения COVID-19. Одна из её задач – считать ежедневное количество заболевших. Из исходных данных у неё есть ежедневное количество заболевших по странам: + +```ruby +data = [ + ['11-9-2020', 'france', 10_000], + ['11-10-2020', 'usa', 35_000], + ['13-12-2020', 'india', 55_000], + ['12-11-2020', 'france', 13_000], + ['12-12-2020', 'usa', 22_000], + ['11-12-2020', 'india', 54_000], +] +``` + +Ваша задача — помочь им написать функцию `sort_cases()`, которая сортирует записи по дате в обратном порядке и возвращает наружу получившийся массив: + +```ruby +result = sort_cases(data) +# result= [ +# ['13-12-2020', 'india', 55_000], +# ['12-12-2020', 'usa', 22_000], +# ['11-12-2020', 'india', 54_000], +# ['12-11-2020', 'france', 13_000], +# ['11-10-2020', 'usa', 35_000], +# ['11-9-2020', 'france', 10_000] +# ] +``` + +## Подсказки + +* Ключом сортировки в данном случае выступает unix timestamp, который можно получить из даты, преобразовав его к объекту типа [Time](https://ruby-doc.org/core-3.0.0/Time.html#class-Time-label-Examples) +* [reverse()](https://ruby-doc.org/core-3.0.0/Array.html#method-i-reverse) – метод возвращает элементы массива в обратном порядке diff --git a/modules/20-collections/20-arrays-methods/ru/README.md b/modules/20-collections/20-arrays-methods/ru/README.md new file mode 100644 index 0000000..4c30b9b --- /dev/null +++ b/modules/20-collections/20-arrays-methods/ru/README.md @@ -0,0 +1,38 @@ + +Помимо обычных методов, массивы содержат большое число методов, работающих с блоками. Возьмем для примера метод `sort()`. Он умеет сортировать только на основе сравнения значений. Такая сортировка сработает в случае простых массивов из чисел, но окажется бесполезной в большинстве реальных ситуаций, где процесс сортировки может быть хитрым. Пример: + +```ruby +# Население стран +data = [ + ['france', 140_000], + ['usa', 300_000], + ['germany', 40_000] +] +``` + +Как отсортировать страны по количеству жителей в них? Для таких ситуаций и нужны функции высшего порядка: + +```ruby +# Внутрь передается блок, +# который принимает на вход элемент массива +data.sort_by { |row| row[1] } +# [ +# ['germany', 40_000], +# ['france', 140_000], +# ['usa', 300_000] +# ] + +# Либо, если кода много +data.sort_by do |row| + row[1] +end +``` + +Метод `sort_by()` ожидает, что из блока вернётся значение, по которому нужно выполнить сортировку. Затем, когда значения собраны, этот метод выполняет сортировку, сравнивая значения с помощью оператора `<=>`. + +Вот некоторые примеры подобных функций: + +* [max_by()](https://ruby-doc.org/core-3.0.0/Enumerable.html#method-i-max_by) – поиск максимального по указанному параметру в массиве +* [partition()](https://ruby-doc.org/core-3.0.0/Enumerable.html#method-i-partition) – разделяет массив на два по указанному условию + +Таких функций у массивов десятки. Единственный способ их запомнить – постоянно практиковаться и читать документацию. diff --git a/modules/20-collections/20-arrays-methods/ru/data.yml b/modules/20-collections/20-arrays-methods/ru/data.yml new file mode 100644 index 0000000..43f74ad --- /dev/null +++ b/modules/20-collections/20-arrays-methods/ru/data.yml @@ -0,0 +1,2 @@ +name: Методы массивов +tips: [] diff --git a/modules/20-collections/30-hash-basics/ru/EXERCISE.md b/modules/20-collections/30-hash-basics/ru/EXERCISE.md new file mode 100644 index 0000000..e20b9b7 --- /dev/null +++ b/modules/20-collections/30-hash-basics/ru/EXERCISE.md @@ -0,0 +1,14 @@ + +Реализуйте функцию `get_words_count_by_lang()`, которая считает количество слов в тексте + +```ruby +text = 'php ruby php java javascript go go go' +get_words_count_by_lang text +# { +# 'php' => 2, +# 'ruby' => 1, +# 'java' => 1, +# 'javascript' => 1, +# 'go' => 3 +# } +``` diff --git a/modules/20-collections/30-hash-basics/ru/README.md b/modules/20-collections/30-hash-basics/ru/README.md new file mode 100644 index 0000000..1f15322 --- /dev/null +++ b/modules/20-collections/30-hash-basics/ru/README.md @@ -0,0 +1,53 @@ + +Hash — тип данных, предназначенный для хранения пар *ключ-значение*. Порядок ключей в хеше определен и совпадает с порядком их добавления. + +```ruby +options = {} +# При таком определении ключ становится символом (symbol) +options = { key: 'value' } +options[:key] # 'value' + +# При таком определении ключ становится строкой (string) +options = { 'key' => 'value' } + +# Добавление/замена в хешах +options[:where] = 'code-basics' +options[:who] = 'hexlet' + +options.any? # true +options.empty? # false + +# Возвращает ключи +options.keys # ['key', :where, :who] + +# Возвращает значения +options.values # ['value', 'code-basics', 'hexlet'] + +# Обращение к несуществующему элементу возвращает nil +options[:language] # nil + +# Доступ к вложенным структурам без необходимости проверок +options.dig(:key1, :key2, :key3) + +# Если ключа нет, то будет брошено исключение +options.fetch(:key) # KeyError (key not found: :key) + +# Если ключа нет, то вернётся значение по умолчанию +options.fetch(:key, 'default value') +``` + +```ruby +# Проверка наличия ключа +options.key?('who') # false +options.key?(:who) # true +``` + +Хеши в Ruby передаются по ссылке, но сравнение происходит по значению, как и в массивах. + +```ruby +options1 = { language: 'ruby' } +options2 = { language: 'ruby' } +options1 == options2 # true +``` + +У хешей, как и у массивов, огромное количество различных методов на все случаи жизни — включая обход и разнообразные функции высшего порядка. Частично мы их коснемся дальше, но основная часть познается уже в повседневной работе. diff --git a/modules/20-collections/30-hash-basics/ru/data.yml b/modules/20-collections/30-hash-basics/ru/data.yml new file mode 100644 index 0000000..52aba96 --- /dev/null +++ b/modules/20-collections/30-hash-basics/ru/data.yml @@ -0,0 +1,4 @@ +name: Хеши +tips: + - | + [each()](https://ruby-doc.org/core-3.0.0/Enumerator.html#method-i-each) diff --git a/modules/20-collections/40-hash-methods/ru/EXERCISE.md b/modules/20-collections/40-hash-methods/ru/EXERCISE.md new file mode 100644 index 0000000..5b38ade --- /dev/null +++ b/modules/20-collections/40-hash-methods/ru/EXERCISE.md @@ -0,0 +1,36 @@ + +Реализуйте функцию `plainify`, которая принимает на вход список песен сгруппированных по имени группы и возвращает плоский список песен, удобный для вывода на экран. + +```ruby + data = { + 'Queen' => [ + 'Bohemian Rhapsody', + "Don't Stop Me Now" + ], + 'Metallica' => [ + 'Nothing Else Matters' + ], + "Guns N' Roses" => [ + 'Paradise City', + 'November Rain' + ], + 'AC/DC' => [ + 'Thunderstruck', + 'Back In Black', + 'Shoot to Thrill' + ] + } + +result = plainify data + +# [ +# { band: 'Queen', song: 'Bohemian Rhapsody' }, +# { band: 'Queen', song: "Don't Stop Me Now" }, +# { band: 'Metallica', song: 'Nothing Else Matters' }, +# { band: "Guns N' Roses", song: 'Paradise City' }, +# { band: "Guns N' Roses", song: 'November Rain' }, +# { band: 'AC/DC', song: 'Thunderstruck' }, +# { band: 'AC/DC', song: 'Back In Black' }, +# { band: 'AC/DC', song: 'Shoot to Thrill' } +# ] +``` diff --git a/modules/20-collections/40-hash-methods/ru/README.md b/modules/20-collections/40-hash-methods/ru/README.md new file mode 100644 index 0000000..01f23f3 --- /dev/null +++ b/modules/20-collections/40-hash-methods/ru/README.md @@ -0,0 +1,86 @@ + +Как и у массивов, базовый метод обработки хешей это `each` (или алиас `each_pair`). Он принимает на вход блок, в который передаются и ключ, и значение: + +```ruby +data = { + clojure: 'dynamic', + go: 'static', + kotlin: 'static' +} + +data.each do |key, value| + # логика +end +``` + +С его помощью можно выполнить любую задачу по преообразованию хеша, но в каждой конкретной ситуации лучше пользоваться специализированными функциями, которые позволяют сократить код и сделать его понятнее. + +**[transform_values](https://ruby-doc.org/core-3.0.0/Hash.html#method-i-transform_values)** + +Этот метод позволяет преобразовать значения, оставив ключи. На выходе получается новый хеш: + +```ruby +new_data = data.transform_values { |value| value.upcase } +# { clojure: "DYNAMIC", go: "STATIC", kotlin: "STATIC" } +``` + +С помощью этого метода можно даже поменять тип значений и сделать их, например, массивами. Точно такой же метод есть и для ключей: [transform_keys](https://ruby-doc.org/core-3.0.0/Hash.html#method-i-transform_keys). + +**[slice](https://ruby-doc.org/core-3.0.0/Hash.html#method-i-slice)** + +Слайс позволяет извлечь из хеша только его часть по указанным ключам: + +```ruby +data.slice(:clojure, :go) +# { clojure: "dynamic", go: "static" } +``` + +**[select](https://ruby-doc.org/core-3.0.0/Hash.html#method-i-select)** + +Для более сложных ситуаций подходит метод `select` — он действует как фильтр и извлекает из хеша его часть, которая соответствует нужным условиям: + +```ruby +data.select { |key, value| value == 'static' } +# { go: "static", kotlin: "static" } +``` + +**[empty?](https://apidock.com/ruby/Hash/empty%3F)** + +**[key?](https://apidock.com/ruby/DBM/key%3F)** + +Иногда бывает полезно проверить хеш на пустоту, за это отвечает метод `empty?` и проверить в хеше наличие ключа - это метод `key?`: + +```ruby +data.empty? # false +data.clear # метод clear очищает хеш +data.empty? # true + +data.key? :go # true +``` + +Важно понимать, что если мы в качестве ключа передадим строку 'go' методу `key?`, то метод вернет `false`. + +**[merge](https://apidock.com/ruby/Hash/merge)** + +Метод `merge` позволяет объединить два хеша. Если в целевом хеше обнаружен дубликат, то он будет перезаписан: + +```ruby +with_ruby = data.merge(ruby:'dynamic') +puts whith_ruby +# => { clojure: "dynamic", go: "static", kotlin: "static", ruby: "dynamic" } +new_hash = data.merge(go:'compiled') +puts new_hash +# => { clojure: "dynamic", go: "compiled", kotlin: "static", ruby: "dynamic" } +``` + +`merge` может принимать блок, который можно использовать например для устранения коллизий: + +```ruby +data = { password: '123456' } +new_data = { password: '123' } +data.merge(new_data) do |key, old_password, new_password| + new_password.length >= 6 ? new_password : old_password +end +``` + +В Ruby для работы с хешами есть методы на все случаи жизни, поэтому почаще заглядывайте в документацию и экспериментируйте. diff --git a/modules/20-collections/40-hash-methods/ru/data.yml b/modules/20-collections/40-hash-methods/ru/data.yml new file mode 100644 index 0000000..d3b46d6 --- /dev/null +++ b/modules/20-collections/40-hash-methods/ru/data.yml @@ -0,0 +1,2 @@ +name: Методы хешей +tips: [] diff --git a/modules/20-collections/50-destructuring/ru/EXERCISE.md b/modules/20-collections/50-destructuring/ru/EXERCISE.md new file mode 100644 index 0000000..8a99924 --- /dev/null +++ b/modules/20-collections/50-destructuring/ru/EXERCISE.md @@ -0,0 +1,19 @@ + +Реализуйте функцию `convert()`, которая преобразует переданные на вход данные по примеру: + +```ruby +data = [ + ['ruby', 'dynamic', 'strong'], + ['js', 'dynamic', 'weak'], + ['c', 'static', 'weak'], + ['kotlin', 'static', 'strong'] +] +# Удаляет вид типизации (второй элемент в массиве) +result = convert(data) +# [ +# ['ruby', 'strong'], +# ['js', 'weak'], +# ['c', 'weak'], +# ['kotlin', 'strong'] +# ] +``` diff --git a/modules/20-collections/50-destructuring/ru/README.md b/modules/20-collections/50-destructuring/ru/README.md new file mode 100644 index 0000000..10abb57 --- /dev/null +++ b/modules/20-collections/50-destructuring/ru/README.md @@ -0,0 +1,70 @@ + +Destructuring – синтаксический сахар для разложения составных данных на элементы. Удобная возможность языка, которая повышает читабельность кода, и немного сокращает его количество. Пример: + +```ruby +data = ['hexlet', 'online courses', topics: ['php', 'js']] +name, description, topics = data +puts name # "hexlet" +puts description # "online courses" +puts topics # { topics: ["php", "js"] } + +# Обычный способ +# name = data[0] либо data.first +# description = data[1] +# topics = data[2] либо data.last +``` + +Имена переменных `name` и `description` могут быть любыми, на деструктуризацию это не влияет. Стоит запомнить, что для деструктуризации вложенных массивов нам нужно знать либо количество элементов в массиве, либо пропустить их, чтобы не получить исключение. Пример: + +```ruby +data = ['hexlet', 'online courses', ['php', 'js']] +# для доступа ко вложенному массиву используются круглые скобки +name, description, (first_topic, second_topic) = data # учли количество элементов + +# _ – используется, когда значение не важно +_, _, (first_topic, _) = data # так же все элементы учтены + +# * – указывает, что нам не важны все значения +*, (_, second_topic) = data # пропустили элементы в первом массиве, во вложенном количество учтено + +# данный пример вызовет исключение: не учли количество элементов +name, description, (first_topic) = data # Error +``` + +Часто деструктуризацию используют для отделения первого (или первых) элемента от остальных. Для этого используется *splat-оператор* – \*. + +```ruby +data = ['hexlet', 'online courses', ['php', 'js']] +name, *rest = data +puts rest # ["online courses", ["php", "js"]] + +# В любом месте +name, *rest, topic = data +puts rest # ["online courses"] +``` + +Деструктуризация, к сожалению, не работает для хешей. Однако можно немного схитрить и получить похожую функциональность: + +```ruby +data = { + name: 'hexlet', + description: 'online courses', + topic: 'programming' +} + +# values_at возвращает массив из значений для указанных ключей +name, description = data.values_at(:name, :description) +``` + +Деструктуризация может применяться везде, включая параметры функций и блоков: + +```ruby +# разложен первый аргумент +# обязательны скобки +def process((name, *rest)) + # логика +end + +data = ['hexlet', 'online courses', 'programming'] +process(data) +``` diff --git a/modules/20-collections/50-destructuring/ru/data.yml b/modules/20-collections/50-destructuring/ru/data.yml new file mode 100644 index 0000000..30d41ac --- /dev/null +++ b/modules/20-collections/50-destructuring/ru/data.yml @@ -0,0 +1,2 @@ +name: Деструктуризация +tips: [] diff --git a/modules/25-functions/10-functions-definition-splat/ru/EXERCISE.md b/modules/25-functions/10-functions-definition-splat/ru/EXERCISE.md new file mode 100644 index 0000000..d756b15 --- /dev/null +++ b/modules/25-functions/10-functions-definition-splat/ru/EXERCISE.md @@ -0,0 +1,14 @@ + +Реализуйте функцию `merge_all()`, которая принимает на вход любое количество хешей и сливает (мержит) их в один хеш, который возвращается наружу: + +```ruby +hash1 = { key: 'value' } +hash2 = { key2: 'value2' } +hash3 = { key3: 'value3', key: 'new value' } +hash = merge_all(hash1, hash2, hash3) +# { key: 'new value', key2: 'value2', key3: 'value3' } +``` + +## Подсказки + +* [merge()](https://ruby-doc.org/core-3.0.0/Hash.html#method-i-merge) diff --git a/modules/25-functions/10-functions-definition-splat/ru/README.md b/modules/25-functions/10-functions-definition-splat/ru/README.md new file mode 100644 index 0000000..995b105 --- /dev/null +++ b/modules/25-functions/10-functions-definition-splat/ru/README.md @@ -0,0 +1,36 @@ + +Splat-оператор в определениях функций позволяет «сворачивать» аргументы в массив. С его помощью создают функции, которые имеют бесконечное количество параметров: + +```ruby +def sum(*numbers) + numbers.sum +end + +# Параметры не обязательны +sum() # 0 +sum(1) # 1 +sum(1, 10) # 11 +sum(1, 10, 8, 1) # 20 +``` + +Splat-оператор комбинируется с обычными параметрами. Например, если мы хотим требовать передачи как минимум одного параметра в функцию `sum()`, то для этого достаточно добавить обычный параметр: + +```ruby +def sum(number, *numbers) + number + numbers.sum +end + +sum() # ArgumentError +sum(1) # 1 +``` + +Splat-оператор может встречаться в определении функции только один раз: + +```ruby +# Так не сработает +def sum(*numbers1, *numbers2) + +# А так сработает +def sum(*number1, number2) +def sum(number1, number2, *numbers) +``` diff --git a/modules/25-functions/10-functions-definition-splat/ru/data.yml b/modules/25-functions/10-functions-definition-splat/ru/data.yml new file mode 100644 index 0000000..c6c40db --- /dev/null +++ b/modules/25-functions/10-functions-definition-splat/ru/data.yml @@ -0,0 +1,2 @@ +name: Упаковка аргументов функции +tips: [] diff --git a/modules/25-functions/20-functions-object-as-last-argument/ru/EXERCISE.md b/modules/25-functions/20-functions-object-as-last-argument/ru/EXERCISE.md new file mode 100644 index 0000000..2e1dd4e --- /dev/null +++ b/modules/25-functions/20-functions-object-as-last-argument/ru/EXERCISE.md @@ -0,0 +1,14 @@ + +Реализуйте функцию `link_to()`, которая генерирует html-ссылки. Функция принимает на вход три параметра: + +* Текст ссылки +* Сама ссылка +* Хеш с атрибутами (не обязательный) + +```ruby +link_to 'name', '/url' +# name + +link_to 'name', '/url', class: 'link' +# name +``` diff --git a/modules/25-functions/20-functions-object-as-last-argument/ru/README.md b/modules/25-functions/20-functions-object-as-last-argument/ru/README.md new file mode 100644 index 0000000..6a3de86 --- /dev/null +++ b/modules/25-functions/20-functions-object-as-last-argument/ru/README.md @@ -0,0 +1,28 @@ + +Одна из идиоматических конструкций в Ruby – функция, принимающая последним параметром хеш. Обычно этот хеш содержит опции. Такое часто встречается и во встроенных функциях, и в библиотеках: + +```ruby +# link_to(body, url, html_options = {}) +link_to 'blog', 'https://ru.hexlet.io/blog', { id: 'news', class: 'article' } +# blog +``` + +В Ruby добавлен «синтаксический сахар», позволяющий опускать скобки в подобных ситуациях. Вызов выше в реальном коде выглядит так: + +```ruby +link_to 'blog', 'https://ru.hexlet.io/blog', id: 'news', class: 'article' +``` + +К такому способу записи нужно немного привыкнуть, так как может быть не сразу понятно, что к чему относится. Но потом станет видно, насколько он удобен и практичен. Это ещё одна из его «фишек», которая помогает строить [языки предметной области](https://github.com/aasm/aasm) (DSL). Из-за небольшого количества дополнительного синтаксиса код на Ruby может выглядеть просто, как текст. Ниже пример роутинга (отвечает за формирование адресов страниц сайта) из Rails: + +```ruby +# В большинстве других языков подобная конструкция либо невозможна, +# либо получится в разы сложнее и загроможденнее символами +resources :companies, only: [:show, :edit, :update, :destroy] do + resources :orders, only: [:create] do + member do + get :payment + end + end +end +``` diff --git a/modules/25-functions/20-functions-object-as-last-argument/ru/data.yml b/modules/25-functions/20-functions-object-as-last-argument/ru/data.yml new file mode 100644 index 0000000..053e8f0 --- /dev/null +++ b/modules/25-functions/20-functions-object-as-last-argument/ru/data.yml @@ -0,0 +1,2 @@ +name: Хеш как последний параметр функции +tips: [] diff --git a/modules/25-functions/30-functions-pipeline/ru/EXERCISE.md b/modules/25-functions/30-functions-pipeline/ru/EXERCISE.md new file mode 100644 index 0000000..b759315 --- /dev/null +++ b/modules/25-functions/30-functions-pipeline/ru/EXERCISE.md @@ -0,0 +1,26 @@ + +Реализуйте функцию `get_men_count_by_year()`, которая принимает список пользователей и возвращает объект, где ключ – это год рождения, а значение – это количество мужчин, родившихся в этот год. + +```ruby +users = [ + { name: 'Bronn', gender: 'male', birthday: '1973-03-23' }, + { name: 'Reigar', gender: 'male', birthday: '1973-11-03' }, + { name: 'Eiegon', gender: 'male', birthday: '1963-11-03' }, + { name: 'Sansa', gender: 'female', birthday: '2012-11-03' }, + { name: 'Jon', gender: 'male', birthday: '1980-11-03' }, + { name: 'Robb', gender: 'male', birthday: '1980-05-14' }, + { name: 'Tisha', gender: 'female', birthday: '2012-11-03' }, + { name: 'Rick', gender: 'male', birthday: '2012-11-03' }, + { name: 'Joffrey', gender: 'male', birthday: '1999-11-03' }, + { name: 'Edd', gender: 'male', birthday: '1973-11-03' } +] + +get_men_count_by_year(users) +# { +# '1973' => 3, +# '1963' => 1, +# '1980' => 2, +# '2012' => 1, +# '1999' => 1 +# } +``` diff --git a/modules/25-functions/30-functions-pipeline/ru/README.md b/modules/25-functions/30-functions-pipeline/ru/README.md new file mode 100644 index 0000000..e9dd344 --- /dev/null +++ b/modules/25-functions/30-functions-pipeline/ru/README.md @@ -0,0 +1,49 @@ + +Функции высшего порядка — это основной способ обработки коллекций во многих языках. И Ruby здесь не исключение. Идиоматический код на Ruby выглядит, как цепочка методов, преобразующих коллекцию в какой-то нужный выход. Такая обработка строится на базе методов `map()`, `filter()`, `reduce()` и их более специфичных аналогах. + +Краткий обзор: + +```ruby +# Обходит коллекцию и вызывает для каждого элемента блок +# Результат складывает в новую коллекцию, которая возвращается +[1, 2, 3].map { |v| v ** 2 } # [1, 4, 9] + +# Фильтрует коллекцию от элементов не удовлетворяющих условию +[1, 2, 3].filter { |v| v.odd? } # [1, 3] + +# Обходит коллекцию, накапливая в процессе аккумулятор (acc) +# После обработки каждого элемента возврат из блока становится +# новым аккумулятором. В конце он возвращается наружу +# Начальное значение аккумулятора передается первым аргументом +[1, 2, 3].reduce(0) { |acc, v| acc + v } # 6 +``` + +При таком подходе вся обработка разбивается на множество небольших действий, которые комбинируются друг с другом, превращаясь в цепочку. Фактически это функциональное программирование на объектах. Оно популярно в большинстве современных языков, поэтому здесь не разбирается подробно. + +Рассмотрим несколько примеров разных преобразований, которые можно выполнять с помощью этих функций с данными, приближенными к реальным. + +```ruby +users = [ + { name: 'Bronn', gender: 'male', birthday: '1973-03-23' }, + { name: 'Reigar', gender: 'male', birthday: '1973-11-03' }, + { name: 'Tisha', gender: 'female', birthday: '2012-11-03' }, + { name: 'Sansa', gender: 'female', birthday: '2012-11-03' }, + { name: 'Rick', gender: 'male', birthday: '2012-11-03' }, +] + +# Формируем список пользователей младше 30 лет +users.filter { |u| Time.now.year - Time.new(u[:birthday]).year < 30 } + +# Формируем список тех же пользователей, но без gender +# Обратите внимание на двойные фигурные скобки — +# внешние для блока, внутренние для хеша +users.map { |u| { name: u[:name], birthday: u[:birthday] } } + +# Группируем пользователей по полу +# Аналогичного результата можно добиться с помощью group_by +users.reduce({}) do |acc, u| + acc[u[:gender]] ||= [] + acc[u[:gender]] << u + acc # обязательно вернуть новый acc +end +``` diff --git a/modules/25-functions/30-functions-pipeline/ru/data.yml b/modules/25-functions/30-functions-pipeline/ru/data.yml new file mode 100644 index 0000000..f79edc8 --- /dev/null +++ b/modules/25-functions/30-functions-pipeline/ru/data.yml @@ -0,0 +1,2 @@ +name: Пайплайн +tips: [] diff --git a/modules/25-functions/40-functions-blocks-as-objects/ru/EXERCISE.md b/modules/25-functions/40-functions-blocks-as-objects/ru/EXERCISE.md new file mode 100644 index 0000000..69f12c2 --- /dev/null +++ b/modules/25-functions/40-functions-blocks-as-objects/ru/EXERCISE.md @@ -0,0 +1,12 @@ + +Реализуйте функцию `apply_blocks(data, blocks)`, которая принимает на вход данные и массив блоков. Эта функция должна по цепочке вызывать блоки, передавая туда результат предыдущего вызова блока: + +```ruby +proc1 = proc { |x| x + 1 } +proc2 = proc { |x| x * 2 } + +apply_blocks(5, [proc1, proc2]) # 12 +# proc2.call(proc1.call(5)) +apply_blocks(5, [proc2, proc1]) # 11 +# proc1.call(proc2.call(5)) +``` diff --git a/modules/25-functions/40-functions-blocks-as-objects/ru/README.md b/modules/25-functions/40-functions-blocks-as-objects/ru/README.md new file mode 100644 index 0000000..8998d56 --- /dev/null +++ b/modules/25-functions/40-functions-blocks-as-objects/ru/README.md @@ -0,0 +1,43 @@ + +Блоки — одна из ключевых концепций в Ruby, без которой практически не обходится ни один кусок кода. Они очень похожи на обычные лямбда-функции, но имеют свои особые черты. Для хорошего понимания языка и происходящего в коде нужно понимать, как они устроены. Здесь мы немного копнем вглубь. + +С одной стороны, у блоков есть особый синтаксис создания и передачи в функции как особого параметра. + +```ruby +# Открытие файла на запись и добавление туда строки +File.open('log.txt', 'w') { |f| f.write "[hexlet] #{Time.now} - User logged in\n" } +``` + +С другой стороны, сам блок — это объект, как и всё остальное в языке. Его можно как создать независимо от функции, так и использовать. За блоки в Ruby отвечает класс `Proc`: + +```ruby +# Немного взрывает мозг то, что блок определяется через свой же синтаксис +square = Proc.new { |x| x**2 } +# Альтернативный синтаксис — proc { |x| x**2 } +square.call(4) # 16 +``` + +С объектом-блоком можно делать всё то же самое, что и с другими объектами. В этом смысле он ведет себя как анонимная функция в любом языке. Однако, если мы захотим этот объект использовать как блок при передаче в функцию, то ничего не получится: + +```ruby +[1, 2].map square +# ArgumentError (wrong number of arguments (given 1, expected 0)) +``` + +Хотя мы и имеем дело с блоком, всё же в примере выше он передается в функцию как обычный объект первым параметром. Но метод `map()` не принимает на вход ничего, кроме блока, поэтому код завершается с ошибкой. Блок, созданный как объект, невозможно напрямую использовать в методах, ожидающих на вход блоки. Для этого нужен специальный синтаксис: + +```ruby +[1, 2].map &square # [1, 4] +``` + +Амперсанд, добавленный в начале переменной, содержащей блок, передает этот блок в функцию не как параметр, а как блок. Но тут нас ждет сюрприз: + +```ruby +# Завершится с ошибкой +[1, 2].map() &square + +# Сработает +[1, 2].map(&square) +``` + +Это поведение не должно вас вводить в заблуждение. Выше мы видим исключительно особенности синтаксиса. Блок выше не передается как параметр, это легко увидеть, если посмотреть на определение метода `map()`. Он не принимает на вход никаких параметров. diff --git a/modules/25-functions/40-functions-blocks-as-objects/ru/data.yml b/modules/25-functions/40-functions-blocks-as-objects/ru/data.yml new file mode 100644 index 0000000..2de7bdd --- /dev/null +++ b/modules/25-functions/40-functions-blocks-as-objects/ru/data.yml @@ -0,0 +1,2 @@ +name: Блоки как объекты +tips: [] diff --git a/modules/25-functions/50-functions-symbols-to-blocks/ru/EXERCISE.md b/modules/25-functions/50-functions-symbols-to-blocks/ru/EXERCISE.md new file mode 100644 index 0000000..310f9c2 --- /dev/null +++ b/modules/25-functions/50-functions-symbols-to-blocks/ru/EXERCISE.md @@ -0,0 +1,13 @@ + +Реализуйте функцию `convert()`, которая принимает на вход список строк и применяет к нему следующую цепочку действий: + +* Список сортируется +* Фильтрация: удаляются строки, не заканчивающиеся на `'?'` +* Отображение: все строки приводятся к нижнему регистру + +Результат возвращается наружу: + +```ruby +strings = ['wow?', 'One?', 'tWo!', 'THREE'] +convert(strings) # ["one?", "wow?"] +``` diff --git a/modules/25-functions/50-functions-symbols-to-blocks/ru/README.md b/modules/25-functions/50-functions-symbols-to-blocks/ru/README.md new file mode 100644 index 0000000..ccebeeb --- /dev/null +++ b/modules/25-functions/50-functions-symbols-to-blocks/ru/README.md @@ -0,0 +1,31 @@ + +В Ruby-коде можно встретить странную конструкцию из амперсанда, соединенного с символом: + +```ruby +['hexlet', 'code-basics'].map(&:upcase) # ["HEXLET", "CODE-BASICS"] +# То же самое +# ['hexlet', 'code-basics'].map { |name| name.upcase } +``` + +Амперсанд в этом выражении обозначает передачу блока в функцию. Но символ — это не блок. Как работает такой код? Всё дело в приведении типов. У символов определен метод `to_proc()`, который преобразует символ в блок определенного вида. Он вызывается автоматически в тех случаях, когда данные используются как блоки. Это то же самое, что и интерполяция данных в строку. + +В отличие от простых типов данных, преобразование символа в блок работает не очевидно. Проще показать на примере: + +```ruby +block = :capitalize.to_proc +# block = proc { |value| value.capitalize } +block.call('hexlet') # "Hexlet" +``` + +То есть получившийся блок принимает на вход один параметр, у которого затем вызывается метод с именем исходного символа. Такое преобразование не случайно, его создали как раз для удобной работы с функциями высшего порядка: + +```ruby +['hexlet', 'code-basics'].map(&:reverse).map(&:capitalize) +# ["Telxeh", "Scisab-edoc"] +``` + +Этот трюк работает даже для операторов, так как в Ruby большинство операторов всего лишь методы: + +```ruby +[1, 3, 4].reduce &:+ # 8 +``` diff --git a/modules/25-functions/50-functions-symbols-to-blocks/ru/data.yml b/modules/25-functions/50-functions-symbols-to-blocks/ru/data.yml new file mode 100644 index 0000000..06bced0 --- /dev/null +++ b/modules/25-functions/50-functions-symbols-to-blocks/ru/data.yml @@ -0,0 +1,2 @@ +name: Трансляция символов в блоки +tips: [] diff --git a/modules/25-functions/60-functions-yield/ru/EXERCISE.md b/modules/25-functions/60-functions-yield/ru/EXERCISE.md new file mode 100644 index 0000000..6f82fe5 --- /dev/null +++ b/modules/25-functions/60-functions-yield/ru/EXERCISE.md @@ -0,0 +1,13 @@ + +Реализуйте функцию `my_filter()`, которая умеет фильтровать переданную на вход коллекцию по условию в блоке: + +```ruby +coll = [1, 2, 3] +my_filter(coll) { |v| v.even? } # [2] +my_filter(coll) { |v| v.odd? } # [1, 3] + +# Примеры выше можно записать и так +# & – преобразует символ в блок и передаст его как блок +my_filter(coll, &:even?) +my_filter(coll, &:odd?) +``` diff --git a/modules/25-functions/60-functions-yield/ru/README.md b/modules/25-functions/60-functions-yield/ru/README.md new file mode 100644 index 0000000..48be993 --- /dev/null +++ b/modules/25-functions/60-functions-yield/ru/README.md @@ -0,0 +1,73 @@ + +Написать в Ruby функцию, которая принимает блок, не так страшно, как может показаться на первый взгляд. В самом простом случае нужно знать про одно ключевое слово – `yield`. Именно с его помощью происходит вызов блока: + +```ruby +def foo() + yield +end + +# Передача блока, который не принимает аргументов +foo { puts 'code-basics for the brave' } +# => "code-basics for the brave" + +# Или так +foo do + puts 'code-basics for the brave' +end +# => "code-basics for the brave" +``` + +Как видно из примера выше, мы не управляем блоком явно. `yield` автоматически получает доступ к блоку и вызывает его. Количество вызовов `yield` может быть любым. Каждый вызов работает, как независимый вызов блока: + +```ruby +def foo() + yield + yield +end + +foo { puts 'Hello Hexlet' } +# => "Hello Hexlet" +# => "Hello Hexlet" +``` + +`yield` можно воспринимать как вызов блока, который во многом ведет себя как обычная функция. Например, если блок возвращает результат, то его же вернет и `yield`. + +```ruby +def foo() + yield +end + +# Передача блока, который не принимает аргументов +# Блок возвращает строку +result = foo { 'code-basics for the brave' } +puts result +# => "code-basics for the brave" +``` + +Если блок содержит параметры, то они передаются в блок через `yield`, как через вызов функции: + +```ruby +def foo(value) + yield(value) +end + +# Параметр из определения функции переходит в блок через yield +foo('Hexlet') { |v| puts v } +# => "Hexlet" +``` + +И, собирая все вместе, так выглядит реализация `map()` внутри: + +```ruby +// В виде функции, а не метода +def my_map(coll) + result = [] + coll.each do |elem| + result << yield(elem) + end + result +end + +my_map([1, 2, 3]) { |v| v * 2 } +# [2, 4, 6] +``` diff --git a/modules/25-functions/60-functions-yield/ru/data.yml b/modules/25-functions/60-functions-yield/ru/data.yml new file mode 100644 index 0000000..08ef8db --- /dev/null +++ b/modules/25-functions/60-functions-yield/ru/data.yml @@ -0,0 +1,2 @@ +name: Блоки внутри функции +tips: [] diff --git a/modules/40-arrays/10-each-with-object/ru/EXERCISE.md b/modules/40-arrays/10-each-with-object/ru/EXERCISE.md new file mode 100644 index 0000000..9922960 --- /dev/null +++ b/modules/40-arrays/10-each-with-object/ru/EXERCISE.md @@ -0,0 +1,14 @@ + +Реализуйте функцию `words_by_letters()`, которая разбирает текст и группирует слова по буквам, отсортированным по алфавиту: + +```ruby +sentence = 'hexlet helps people to become developers' +result = words_by_letters(sentence) +# { +# b: ['become'], +# d: ['developers'], +# h: ['helps', 'hexlet'], +# p: ['people'], +# t: ['to'] +# } +``` diff --git a/modules/40-arrays/10-each-with-object/ru/README.md b/modules/40-arrays/10-each-with-object/ru/README.md new file mode 100644 index 0000000..75427ea --- /dev/null +++ b/modules/40-arrays/10-each-with-object/ru/README.md @@ -0,0 +1,25 @@ + +В некоторых ситуациях использование `reduce()` лучше заменить на метод `each_with_object()`, который работает похоже, но не требует возврата аккумулятора. В таком случае кода получается меньше, и он чуть проще. Посмотрим на классический пример подсчёта вхождения слов в предложение: + +```ruby +sentence = 'hexlet – work harder' +words = sentence.split +result = words.reduce({}) do |acc, word| + acc[word] ||= 0 + acc[word] += 1 + acc # <= возврат +end +``` + +И то же самое с `each_with_object()`: + +```ruby +sentence = 'hexlet – work harder' +words = sentence.split ' ' +result = words.each_with_object({}) do |word, acc| + acc[word] ||= 0 + acc[word] += 1 +end +``` + +Не сказать, что код изменился кардинально, но всё же упростился. Этот метод встречается в разработке достаточно часто и, более того, рекомендуется линтером в тех случаях, где он может заменить свёртку. diff --git a/modules/40-arrays/10-each-with-object/ru/data.yml b/modules/40-arrays/10-each-with-object/ru/data.yml new file mode 100644 index 0000000..e672ff6 --- /dev/null +++ b/modules/40-arrays/10-each-with-object/ru/data.yml @@ -0,0 +1,2 @@ +name: each_with_object +tips: [] diff --git a/modules/40-arrays/40-arrays-as-sets/ru/EXERCISE.md b/modules/40-arrays/40-arrays-as-sets/ru/EXERCISE.md new file mode 100644 index 0000000..43f5958 --- /dev/null +++ b/modules/40-arrays/40-arrays-as-sets/ru/EXERCISE.md @@ -0,0 +1,24 @@ + +Иногда в программировании возникает задача поиска разницы между двумя наборами данных, такими как объекты. Например, при поиске различий в json-файлах. Для этого даже существуют специальные сервисы, например, http://www.jsondiff.com/ (попробуйте нажать на ссылку sample data и кнопку Compare). + +Реализуйте функцию `gen_diff()`, которая сравнивает два объекта и возвращает результат сравнения в виде объекта. Ключами результирующего объекта будут все ключи из двух входящих объектов, а значением — строка с описанием отличий: added, deleted, changed или unchanged. + +Возможные значения: + +* added — ключ отсутствовал в первом объекте, но был добавлен во второй +* deleted — ключ был в первом объекте, но отсутствует во втором +* changed — ключ присутствовал и в первом, и во втором объектах, но значения отличаются +* unchanged — ключ присутствовал с одинаковыми значениями и в первом, и во втором объектах + +```ruby +gen_diff( + { one: 'eon', two: 'two', four: true }, + { two: 'own', zero: 4, four: true } +); +# { +# one: 'deleted', +# two: 'changed', +# four: 'unchanged', +# zero: 'added' +# } +``` diff --git a/modules/40-arrays/40-arrays-as-sets/ru/README.md b/modules/40-arrays/40-arrays-as-sets/ru/README.md new file mode 100644 index 0000000..baafd8b --- /dev/null +++ b/modules/40-arrays/40-arrays-as-sets/ru/README.md @@ -0,0 +1,53 @@ + +В работе над массивами есть особый набор операций, который пришёл к нам из математики – это операции над множествами. В Ruby для такого случая есть специальные операторы, близкие к их математическим эквивалентам. + +Представьте себе задачу поиска взаимных друзей пользователей. Для формирования такого списка на уровне кода, нужно сравнить два массива (мои друзья и друзья друга) и найти пересечение, то есть общих друзей. В данном случае массивы с друзьями — это множества, а операция поиска общих элементов – пересечение (intersection). + +Пересечение на Ruby выглядит так: + +```ruby +friends1 = ['igor', 'anna', 'nina', 'sergey'] +friends2 = ['igor', 'petya', 'inna', 'anna'] + +# Выглядит как побитовое И, но это пересечение +friends1 & friends2 # ["igor", "anna"] +# или +# friends1.intersection(friends2) +``` + +Такой оператор очень удобен своей естественностью. Сразу понятно, о чём идёт речь. Как и в большинстве других операторов в Ruby, мы имеем дело с вызовами методов: + +```ruby +friends.&(friends2) +``` + +Подобная схема позволяет не только переопределять их поведение, но и комбинировать операторы между собой: + +```ruby +friends = ['anna', 'ivan'] +friends1 & friends2 & friends # ["anna"] +``` + +У множеств и массивов с точки зрения математики есть одно принципиальное отличие, о котором нужно помнить. Во множествах каждый элемент представлен ровно один раз (то есть все элементы — уникальны), в то время как в массивах такого ограничения не существует. Операции над множествами рассматривают массивы не как массивы, а именно как множества. Они удаляют дубли в результирующем массиве: + +```ruby +[1, 3, 4] & [1, 3, 3, 8] # [1, 3] +``` + +*Объединение* + +Множество, объединяющее в себе элементы исходных множеств. + +```ruby + [1, 3, 4] | [1, 3, 3, 8] + # [1, 3, 4, 8] +``` + +*Дополнение* + +Множество, состоящее из элементов первого множества, за минусом элементов, совпадающих со вторым множеством. Или по простому — это разница между двумя множествами. + +```ruby +# 4 – единственный элемент из первого множества, которого нет во втором +[1, 3, 4] - [1, 3, 3, 8] # [4] +``` diff --git a/modules/40-arrays/40-arrays-as-sets/ru/data.yml b/modules/40-arrays/40-arrays-as-sets/ru/data.yml new file mode 100644 index 0000000..5e295c2 --- /dev/null +++ b/modules/40-arrays/40-arrays-as-sets/ru/data.yml @@ -0,0 +1,2 @@ +name: Массивы как множества +tips: [] diff --git a/modules/40-arrays/50-arrays-definitions/ru/EXERCISE.md b/modules/40-arrays/50-arrays-definitions/ru/EXERCISE.md new file mode 100644 index 0000000..f40928a --- /dev/null +++ b/modules/40-arrays/50-arrays-definitions/ru/EXERCISE.md @@ -0,0 +1,14 @@ + +Реализуйте функцию `valid_date?()`, которая проверяет переданную дату на допустимость по двум критериям: + +* Месяц находится между 1 и 12 +* День находится между 1 и 31 + +Дата поступает на вход всегда в формате: *dd-mm-yyyy*. Не используйте функции для работы с датами, реализуйте все с помощью обычных типов данных. + +```ruby +valid_date?('11-11-2011') # true +valid_date?('13-11-2011') # true +valid_date?('11-13-2011') # false +valid_date?('55-11-2011') # false +``` diff --git a/modules/40-arrays/50-arrays-definitions/ru/README.md b/modules/40-arrays/50-arrays-definitions/ru/README.md new file mode 100644 index 0000000..cd8eba4 --- /dev/null +++ b/modules/40-arrays/50-arrays-definitions/ru/README.md @@ -0,0 +1,44 @@ + +В Ruby необычно много способов создания массивов. Они помогают сделать определение короче, и достаточно часто используются на практике. Особенно любит их линтер Rubocop. + +```ruby +# Обычное определение +['apple', 'orange', 'melon'] + +# Сокращенное +%w[apple orange melon] +``` + +`%w` позволяет задавать массив перечислением без использования дополнительных символов. Даже в примере выше видно, насколько код получается короче и даже читаемее. Единственное ограничение такого способа – наличие пробелов в значениях. Каждый пробел считается разделителем значений: + +```ruby +%w[first second value third] +# ["first", "second", "value", "third"] +``` + +Технически, слова можно экранировать, но так обычно не делают из-за резкого ухудшения читаемости: + +```ruby +%w[first second\ value third] +# ["first", "second value", "third"] +``` + +Похожим способом создается массив символов, только вместо `%w` используется `%i`: + +```ruby +%i[apple orange melon] # [:apple, :orange, :melon] +``` + +Так же массивы можно создавать из диапазонов или итераторов: + +```ruby +(1..5).to_a # [1, 2, 3, 4, 5] +1.upto(5).to_a # [1, 2, 3, 4, 5] +``` + +В некоторых достаточно редких случаях можно воспользоваться динамическим созданием значений. Для этого используется конструктор Array и блок, вычисляющий значение для нужного индекса: + +```ruby +Array.new(5) { |i| i ** 2 } +# [0, 1, 4, 9, 16] +``` diff --git a/modules/40-arrays/50-arrays-definitions/ru/data.yml b/modules/40-arrays/50-arrays-definitions/ru/data.yml new file mode 100644 index 0000000..d6268e9 --- /dev/null +++ b/modules/40-arrays/50-arrays-definitions/ru/data.yml @@ -0,0 +1,2 @@ +name: Способы определения массивов +tips: [] diff --git a/modules/50-hash/10-hash-fetch/ru/EXERCISE.md b/modules/50-hash/10-hash-fetch/ru/EXERCISE.md new file mode 100644 index 0000000..f65907f --- /dev/null +++ b/modules/50-hash/10-hash-fetch/ru/EXERCISE.md @@ -0,0 +1,16 @@ +Реализуйте функцию `setup_env()`. + +Функция принимает: ключ, хеш и блок который тоже принимает два параметра: хеш и ключ из параметров функции. + +Если переданного ключа нет в хеше, то вызываем блок и передаем в него параметры: хеш и ключ, в результате функция должна вернуть обработанный хеш. + +Eсли ключ присутствует, то функция должна вернуть хеш в неизменном виде. + +```ruby +env = { api_key: 123 } +setup_env(:host, env) { |env, key| env[key] = 'localhost' } # -> { api_key: 123, host: 'localhost' } +setup_env(:api_key, env) { |env, key| env[key] = 'localhost' } # -> { api_key: 123 } +env == { api_key: 123 } # -> true +``` + +Функция не должна мутировать исходный объект. diff --git a/modules/50-hash/10-hash-fetch/ru/README.md b/modules/50-hash/10-hash-fetch/ru/README.md new file mode 100644 index 0000000..8def5f9 --- /dev/null +++ b/modules/50-hash/10-hash-fetch/ru/README.md @@ -0,0 +1,56 @@ +В Ruby значения из хеша по ключу извлекает метод `[]` и `fetch()`. В чем же принципиальное отличие между ними? + +Если запрашивать значение по несуществующему ключу, метод `[]` молча возвращает `nil`. + +```ruby +env = { + host: 'localhost', + port: 3000 +} +puts env[:user] # -> nil +``` + +Метод `fetch()` же действует иначе - если ключа нет, получим ошибку `КеуError`. + +```ruby +env = { + host: 'localhost', + port: 3000 +} +puts env.fetch(:user) # -> получим ошибку key not found (KeyError) +``` + +Но это поведение можно переопределить, так как метод `fetch()` принимает вторым параметром значение по умолчанию: + +```ruby +env = { + host: 'localhost', + port: 3000 +} +puts env.fetch(:user, nil) # -> nil +puts env.fetch(:user, 'guest') # -> 'guest' +``` + +И даже блок или лямбда-функцию: + +```ruby +env = { host: 'localhost' } + +env.fetch(:user) { |key| env[key] = 'guest' } +puts env # -> { host: 'localhost', user: 'guest' } +``` + +Пример конфигурации почтовой рассылки из приложения на Ruby on Rails: + +```ruby +config.action_mailer.smtp_settings = { + user_name: ENV.fetch('MAIL_USERNAME', nil), + password: ENV.fetch('MAIL_PASSWORD', nil), + address: ENV.fetch('MAIL_HOST', nil), + domain: ENV.fetch('MAIL_HOST', nil), + port: ENV.fetch('SMTP_PORT', '25'), + authentication: :cram_md5 +} +``` + +Ruby style guide советует использовать метод `fetch()` вместо метода `[]`. diff --git a/modules/50-hash/10-hash-fetch/ru/data.yml b/modules/50-hash/10-hash-fetch/ru/data.yml new file mode 100644 index 0000000..0ca5546 --- /dev/null +++ b/modules/50-hash/10-hash-fetch/ru/data.yml @@ -0,0 +1,6 @@ +name: Метод fetch и извлечение значений из хеша +tips: + - | + [yield](https://apidock.com/ruby/Proc/yield) + - | + [fetch()](https://apidock.com/ruby/Hash/fetch) diff --git a/modules/50-hash/20-hash-new/ru/EXERCISE.md b/modules/50-hash/20-hash-new/ru/EXERCISE.md new file mode 100644 index 0000000..205aa27 --- /dev/null +++ b/modules/50-hash/20-hash-new/ru/EXERCISE.md @@ -0,0 +1,23 @@ +Напишите функцию `get_adjacency_matrix()`, которая строит упрощенный вариант матрицы смежности из друзей человека в социальной сети. + +Функция принимает массив элементами которого является тоже массивы: + +```ruby +[[Vasy, Pety], + [Vasy, Oksana], + [Vasy, Egor], + [Egor, Pety], + [Egor, Georgiy], + [Oksana, Andrey]] +``` +Вложенный массив состоит из: имени человека - первый элемент и имени его друга - второй элемент. + +Функция должна вернуть хеш, ключем которого будет имя человека, а значением - массив из его друзей. + +```ruby +{ + 'Vasy': ['Pety', 'Oksana', 'Egor'], + 'Egor': ['Pety', 'Georgiy'], + 'Oksana': ['Andrey'] +} +``` diff --git a/modules/50-hash/20-hash-new/ru/README.md b/modules/50-hash/20-hash-new/ru/README.md new file mode 100644 index 0000000..52cad86 --- /dev/null +++ b/modules/50-hash/20-hash-new/ru/README.md @@ -0,0 +1,55 @@ +В этом уроке мы познакомимся с альтернативными способами создания хеша. + +Вспомним как создавать хеш через литерал: + +```ruby +env = {} # пустой хеш +env = { host: 'example.com' } +``` + +В Ruby имеется альтернативный способ создания хеша, через вызов метода `new()` у объекта `Hash`: + +```ruby +env = Hash.new # создали пустой хеш +puts(env[:host]) # -> nil +``` +Такой способ имеет некоторые преимущества, давайте посмотрим какие: + +```ruby +env = Hash.new('localhost') +puts(env[:user]) # -> 'localhost' +``` + +Мы передаем в метод `new()` значение localhost, если мы обратимся к несуществующему ключу, то метод `[]` вернет это значение: + +Такое поведение не распространяется на метод `fetch()`: + +```ruby +env = Hash.new('localhost') +puts(env.fetch(:host)) # -> key not found: :host (KeyError) +``` +Как вы видите, так же будет ошибка: KeyError. + +Где же хранится значение по умолчанию? Это можно посмотреть вызвать метод `inspect()` видно, что значение не является частью хеша, а является частью экземпляра класса Hash: + +```ruby +env = Hash.new('localhost') +puts(env.inspect) # -> {} +``` + +`Hash.new` может принимать блок кода, который также позволяет задавать ключам значение по умолчанию. Таким образом нам не надо проверять существует ключ или нет: + +```ruby +dependencies = Hash.new { |h, key| h[key] = [] } +dependencies[:simple_form] << 'simple_form_bootstrap' +puts(dependencies[:simple_form]) # -> ['simple_form_bootstrap'] +``` +Так же значение по умолчанию можно задавать через метод `default()`: + +```ruby +env = Hash.new('localhost') +puts(env[:host]) # -> localhost +env.default = 'example.com' +puts(env[:host]) # -> example.com +``` +В этом уроке мы научились создавать хеш альтернативным способом через `Hash.new`. Это бывает полезно когда нам надо инициализировать ключи значением по умолчанию. diff --git a/modules/50-hash/20-hash-new/ru/data.yml b/modules/50-hash/20-hash-new/ru/data.yml new file mode 100644 index 0000000..9a602ac --- /dev/null +++ b/modules/50-hash/20-hash-new/ru/data.yml @@ -0,0 +1,4 @@ +name: Hash.new. Задание значений по умолчанию. +tips: + - | + [Hash.new](https://ruby-doc.org/core-2.5.1/Hash.html)