В Ruby много методов, которые применяются не очень часто, и о тех случаях, в которых они задействуются, многие просто не знают, потому что никогда этими методами не пользовались.

Предлагаем вашему вниманию список таких малоизвестных методов Ruby. Так что, когда вам захочется узнать, есть ли в Ruby метод, который пригодится в вашей программе, или когда на глаза попадется какой-то незнакомый метод, у вас под рукой уже будет готовый список со всей необходимой информацией.

Итак, перейдем к списку.

1. Манипуляции с аргументами

1) Деструктурирование аргументов:

def print_from_array((a1,a2))
  print first: a1, second: a2
end

print_from_array(['output1', 'output2']) 
=> {first: 'output1'}, {second: 'output2'}

Когда при определении метода добавляется вторая пара скобок вокруг аргументов, например так => method((a1,a2)), происходит деструктурирование массива, передаваемого в этот метод в качестве аргумента. Смотрите: в строке 5, несмотря на то, что в массив print_from_array передается только один аргумент, метод print_from_array воспринимает a1 и a2 как элементы этого массива и выводит отдельные элементы. (Обратите внимание: с хешем такое не проходит. В примере это def print_from_array({a1,a2}).)

В обратном порядке, когда метод определяется вот так:

def print_from_array((a1,a2))
  print first: a1, second: a2, third: a3
end

print_from_array(['output1', 'output2', 'output3']) 
=> NameError (undefined local variable or method `a3' for main:Object)

возникает ошибка даже при передаче массива из трех элементов.

2) Преобразование аргументов в массив:

def convert_args_to_array(*args)
  print args
end

convert_args_to_array("string1", "string2", "string3")
=> ["string1", "string2", "string3"]

Здесь происходит преобразование в массив любых аргументов, передаваемых в convert_args_to_array, даже когда они передаются как что-то другое.

А еще выполняется такое преобразование:

def convert_some_args_to_array(arg1, *args)
  print arg1, args
end

convert_some_args_to_array("string1", "string2", "string3")
=> string1 ["string2", "string3"]

Первый аргумент отделяется, после чего остальные преобразуются в массив.

2. Методы массивов

1) Оператор пересечения « & »:

array1 = [1,2,3,4,5,6,7,8,9] 
array2 = [1,2,3,4,'a','b','c']
array3 = ['a','b','c','d','e']

array1 & array2 => [1,2,3,4]
array2 & array3 => ['a','b','c']
array1 & array3 => []

В случае когда берутся два массива с целью найти их общие элементы (пересечение), используется оператор &. Возвращается новый массив с элементами, которые есть в обоих этих массивах.

2) Разностный оператор « — » :

array1 = [1,2,3,4,5,6,7,8,9] 
array2 = [1,2,3,4,'a','b','c']
array3 = ['a','b','c','d','e']

array1 - array2 => [5,6,7,8,9]
array2 - array1 => ['a','b','c']
array2 - array3 => [1,2,3,4]
array3 - array2 => ['d','e']

Когда надо определить разницу между двумя массивами, используется разностный оператор. В этом случае возвращается новый массив с элементами, которые есть в первом массиве и которых нет во втором.

3) Метод «rotate»:

array = [1,2,3,4,5]
array.rotate => [2,3,4,5,1]

С помощью этого метода весь массив просто поворачивается таким образом, что первый элемент теперь становится последним.

И есть ещё

4) метод «shuffle»:

array = [1,2,3,4,5]
array.shuffle => [5, 4, 2, 1, 3]
array.shuffle => [3, 4, 5, 1, 2]
array.shuffle => [5, 1, 3, 2, 4]

из которого возвращается новый массив со случайно упорядоченными элементами. Элементы перетасовываются каждый раз заново, так что массив возвращается уже с другой последовательностью элементов. (Подходит для игр и т. п.)

3. Proc.new (или proc) и лямбда (->{блок кода})

Это совсем малоизвестная концепция, однако является очень важной частью Ruby (и многих других языков тоже).

Вот что сказано о ней на ruby-doc.org:

Объект Proc представляет собой инкапсуляцию блока кода, который хранится в локальной переменной, передается методу или другому Proc, и вызывается. Proc  —  это важнейшее понятие в Ruby и сердцевина его возможностей, связанных с функциональным программированием.

Чувствую, что должным образом освятить эту тему не получится, поэтому просто оставлю пару ссылок для тех, кому нужна дополнительная информация.

Proc  —  это фактически блок кода, который сохраняется в переменной и потом вызывается с помощью .call. Выглядит он обычно так:

add_one = proc {|number| number + 1}
add_one.call(5) => 6

Вам должен быть знаком этот синтаксис по другим методам, таким как .map или .filter. Но на самом деле Proc (использующийся как синоним блока кода) задействуют для того, чтобы указать на логику, выполняемую на каждой итерации.

Дальнейший материал неплохо бы читать, предварительно ознакомившись с документацией по Ruby: ruby-docs или rubyguides.com.

По этой теме следует сказать несколько слов об операторе Proc &. В отличие от оператора пересечения, о котором упоминалось ранее в этой статье, при использовании в качестве аргумента для метода вызывается оператор деконструкции блока и указывает методу, что передаваемый аргумент  —  это Proc (блок кода) и должен таковым и считаться.

Вот его типичный синтаксис:

p = proc {|x, y| x + y }
[[1, 2], [3,4]].map(&p) #=> [3, 7]

Обратите внимание: это обычный метод .map и вместо того ,чтобы определять блок кода после map, здесь на первой строке определяется proc и дальше с помощью & передается в map. В map сообщается, что это блок, который map должен использовать для логики, выполняемой на каждой итерации.

Заключение

Вот и все. Мы разобрали несколько малоизвестных, но полезных и интригующих методов и концепций. Хотел бы я узнать о некоторых из них раньше, потому что они пригодились бы мне в тех ситуациях, когда не получалось найти правильный метод для той или иной задачи. И хорошо, что они есть у нас сейчас. А некоторые из них мне показались просто интересными, и теперь, когда они понадобятся, у вас под рукой уже будет готовый список со всеми методами.

Читайте также:

Читайте нас в TelegramVK и Яндекс.Дзен


Перевод статьи Yisrael Gurkow: A compendium of obscure Ruby methods (or maybe just a couple methods/concepts that I didn’t know before…)

Предыдущая статьяOpenTelemetry и Sentry - недооцененные инструменты трассировки распределенных систем на Golang
Следующая статьяОчереди с приоритетом в Java