Контейнеры, блоки и итераторы

Андрей Васильев

2020

Отладка исходного кода

Для отладки работы исходного кода на Ruby существуют следующие способы:

  • Написать небольшую программу, выполняющие методы, и запустить её
  • Использовать irb для запуска методов
  • Написать модульные тесты

irb — интерактивный интерпретатор Ruby, позволяющий выполнять код построчно (REPL, Read-Eval-Print Loop)

Для подключения собственных файлов в irb:

  • Перейдите в каталог с вашим файлом
  • Запустите irb:

    irb -I .
  • Подключите файл в вашу сессию require ...

Использование pry

pry — альтернативная реализация интерактивного интерпретатора Ruby

Установка

pry не входит в поставку Ruby, его необходимо поставить отдельно:

$ gem install pry

Желательно также установить пакет pry-byebug

$ gem install pry-byebug

Отладка кода с помощью pry и pry-byebug

Комбинация pry и pry-byebug позволяет реализовать интерактивную отладку приложения. Для включения отладки достаточно в нужное место добавить следующий код:

Для навигации следует использовать следующие команды:

  • break — управлять списком точек остановки
  • step — сделать 1 или несколько шагов выполнения
  • next — сделать 1 или несколько шагов в данном фрейме
  • finish — выполнить код до завершения фрейма
  • continue — продолжить выполнение и выйти из pry

Документация на byebug: https://github.com/deivid-rodriguez/pry-byebug

Массивы

Обычно массивы создаются с помощью литералов

Но можно и с помощью создания объекта

  • Атрибут #size, также известный как #length, возвращает число элементов в массиве
  • Для массивов рекомендуется использовать #size, а для строк — #length

Доступ к элементам

Для доступа к элементам массива используется метод #[]

  • Элементы массива нумеруются с нуля
  • Метод поддерживает отрицательные индексы, последний элемент имеет индекс -1
  • Результат обращения к несуществующему элементу — nil
  • Даный метод можно переопределить в дочерних классах

Выборка части массива

Метод #[] может принимать два аргумента: начало и количество. Однако лучше использовать метод #slice

  • Первый аргумент указывает позицию, с которой начинается выборка
  • Количество элементов может быть только положительным
  • Результат вызова метода — новый массив, содержащий элементы оригинального массива
  • Если начало выборки вне массива, то результат работы — nil

Использование диапазонов

В Ruby есть встроенный тип Range, диапазон. Для его создания существуют 2 литерала: .. (включающий границы) и ... (исключающий границу справа)

Их можно использовать для выборок массивов

Изменение значений массивов

Для записи значений в массив использется метод #[]=

При присвоении к несуществующему элементу пропуски заполняются nil

Изменение нескольких значений

Метод []= также принимает набор

Такой код невозможно воспринимать ни при каких обстоятельствах, поэтому использовать такую нотацию не рекомендуется

Краткий обзор методов массивов

Официальная документация описывает множество методов класса Array, например:

  • #push, #append — добавить в конец массива
  • #pop — извлечь элемент из конца массива
  • #shift — извлечь элемент с начала массива
  • #unshift, #prepend — добавить в начало массива
  • #first(n) — получить n первых элементов
  • #last(n) — получить n последних элементов
  • #drop(n) — получить элементы массива с позиции n
  • #shuffle — создать новый массив, перемешав элементы
  • #delete(obj) — удалить все вхождения объекта obj

Хеши (ассоциативные массивы)

Хеши описывают соответствие между наборами из двух объектов. Первый объект называется ключом и должен быть уникальным среди всех ключей. Второй объект — значение

Хеши обычно создаются с помощью литералов

Часто ключами хешей являются символы

Поиск наиболее часто встречаемых слов

Подсчитаем, насколько часто встречаются слова в тексте. Для решения этой задачи необходимо:

  • Разбить строку на слова
  • Подсчитать частоту встречи слов
  • Отсортировать по частоте встречи
  • #downcase преобразует строку к нижнему регистру
  • #scan возвращает массив строк, совпадающих с переданным регулярным выражением

Подсчёт частоты с помощью Хешей

Сортировка результатов

Пары ключ-значение сохраняют свой порядок в Хешах, что позволяет их сортировать

Для сортировки можно использовать метод #sort_by, который принимает блок и использует его значения для сортировки

Вывод 5 наиболее часто встречающихся слов

Полезные методы Хешей

  • #has_key? — проверка на наличие ключа
  • #has_value? — проверка на наличие значения
  • #last — получение последних элементов
  • #sort_by — сортировка элементов
  • #length — количество элементов
  • #delete — удалить пару ключ-значение

Блоки и итераторы

Привычный императивный стиль

С применением итераторов исходный код становится легче к восприятию, следовательно содержит меньше ошибок

Блоки

  • Блок — набор выражений, находящийся между ключевыми словами begin и end или фигурными скобками
  • Блок можно назвать «анонимным» методом
  • Блоки могут иметь аргументы, которые указываются между вертикальными линиями
  • Блок не исполняется в том месте, где описан в коде
  • Блок ассоциируется с методом в момент вызова метода
  • Блок описывается после параметров метода

Область видимости переменных

  • Выражения блока имеют доступ к переменным, объявленным вне блока
  • Переменные, объявленные внутри блока, доступны только лишь в выражениях внутри блока
  • Аргументы блока маскируют внешние переменные

Область видимости переменных

Переменные блока маскируют внешние переменные

Можно определить список локальных переменных

Но не надо так делать

Создание итераторов

  • Итератор — метод, вызывающий ассоциированный блок
  • Для вызова блока используется ключевое слово yield

Метод вызывает ассоциированный блок два раза

  • Вызываемому блоку можно передавать параметры
  • У блока можно получать возвращаемое значение

Вычисление последовательности Фибоначи

Блоку передаются параметр — следующее число последовательности Фибоначи. Блок будет вызываться столько раз, сколько необходимо для выполнения условия цикла

Возващение значения из блока

  • Если блок возвращает правдивое значение, тогда #find возвращает значение элемента
  • Метод #find ничего не знает об условии, но эффективно обходит все элементы массива и предоставляет общую структуру для решения задачи

Работа ключевых слов в блоках

  • return не имеет специального смысла в блоках, связан только с методом
  • break заканчивает выполнение блока, а также метода, который его вызвал
  • next завершает текущую итерацию, цикл продолжается со следующей

Возвращение значения из блока

Ключевое слово break принимает аргумент, значение которого становится результатом вызова метода с блоком

Будьте аккуратны при использовании break, так как выполнение метода прерывается

Итератор #map

  • Итератор #map (или #collect) позволяет создать новый массив на основе значений текущего массива
  • Создание каждого элемента нового массива описывается в блоке, ассоциированном с данным методом

Метод String#succ возвращает «преемника» для данной строки, начиная с правого символа строки

Потоки ввода-вывода

Классы ввода-вывода предоставляют итераторы для чтения по линиям или байтам

Итераторы могут быть использованы для решения множества задач

Учёт позиции в итераторе

  • Итератор скрывает позицию элемента в массиве
  • Для учёта позиции используйте метод with_index
  • Блок будет получать ещё и порядковое значение

Данная функциональность основана на работе нумераторов, которые разберём в следующих лекциях

Итераторы, использующие логические значения

Данные итераторы предполагают, что блок будет возвращать логические значения

  • Метод #any? — есть ли хотя бы один элемент, удовлетворяющий условию
  • Метод #all? — все ли элементы удовлетворяют условию
  • Метод #one? — только один элемент удовлетворяет условию
  • Метод #none? — ни один элемент не удовлетворяет условию
  • Метод #find — найти первый элемент, удовлетворяющий условию
  • Метод #find_all — найти все элементы, удовлетворяющие условию
  • Метод #find_index — найти номер первого элемента, удовлетворяющего условию
  • Метод #delete_if — удалить из массива все элементы, удовлетворяющие условию

Вычисление агрегированных значений

Часто необходимо вычислить значение, основываясь на всех элементах массива. Итератор #reduce (или #inject) позволяет решить данную задачу

Можно не указывать начальное значение, тогда первый элемент массива — начальное значение

Можно просто указать метод, который необходимо вызывать у элементов массива

Итераторы #tap и #yield_self

Итератор #tap

Данный итератор позволяет встроиться в цепочку по обработке данных, не изменяя её содержимое. Он передаёт self в качестве аргумента блока и возвращает его в качестве значения вызова метода

Итератор #then, #yield_self

Данный итератор был добавлен в Ruby 2.5 с целью структурировать обработку данных. В версии 2.6 данный метод был переименован в #then