П'ять методів Ruby, які вам слід використовувати
Є щось магічне в Ruby. Ця мова програмування дозволяє вам висловлювати свої ідеї за допомогою комп'ютера. Може бути, що саме тому вона стала такою популярною у сучасній веб-розробці.
В Рубі є багато способів зробити одну і ту саму дію. Я читаю багато технічної літератури та часто стикаюсь з прикладами коду і розумію, що дуже багато складних задач можна вирішити або спростити, знаючи всього-на-всього один метод.
У цій статті ми розглянемо деякі маловідомі Ruby методи ефективного вирішення конкретних задач.
Tap
Ви коли-небудь викликали метод якого-небудь об'єкта,але отримуване значення було не тим, що вам потрібно? Ви сподівалися отримати назад об'єкт, але замість цього повертали деякі інші значення. Можливо, ви хотіли додати довільний набір параметрів, які зберігаються у хеші. Ви оновлювали його за допомогою Hash.[], але отримували назад ‘bar’ замість параметрів хешу, так що ви повинні були повернути його в явному вигляді.
def update_params(params)
params[:foo] = 'bar'
params
end
Params наприкінці методу здається зайвим.
Ми можемо прибрати його за допомогою Tap.
Він простий у використанні. Просто викличіть метод, а потім передайте tap блок з кодом, який ви хочете запустити. Об'єкт буде переданий блоку, а потім повернений. Ось як ми могли б поліпшити update_params:
def update_params(params)
params.tap {|p| p[:foo] = 'bar' }
end
Існують десятки задач, які можуть бути вирішені або спрощені з використанням tap. Так що тримайте очі відкритими для методів, які не повертають об’єкт, коли вам це потрібно.
Bsearch
Я не знаю як ви, але я часто використовую масиви даних. Select, reject і find - цінні інструменти, які я використовую щодня. Але якщо набір даних великий, я починаю турбуватися про час, який буде витрачений, щоб пройти через всі ці записи.
Якщо ви використовуєте ActiveRecord і SQL бази даних, то ви повинні знати, що за лаштунками відбувається багато магії, щоб переконатися, що ваші пошуки ведуться з меншою алгоритмічною складністю. Але іноді вам потрібно витягти всі дані з бази даних, перш ніж ви зможете працювати з нею. Наприклад, якщо записи зашифровані в базі даних, ви не зможете запросити їх з SQL у гарному вигляді.
Коли справа доходить до пошуку даних в масивах в Ruby, перший спосіб, який приходить на розум, це find, також відомий як detect. Однак, цей метод буде перебирати весь список до тих пір, поки не буде знайдено збіг. Добре, якщо запис знаходиться на початку, але як щодо ситуації, коли запис знаходиться в кінці дуже довгого списку.
Є більш швидкий спосіб. За допомогою bsearch.
Подивіться на різницю між двома підходами при пошуку в діапазоні 50 000 000 чисел:
require 'benchmark'
data = (0..50_000_000)
Benchmark.bm do |x|
x.report(:find) { data.find {|number| number > 40_000_000 } }
x.report(:bsearch) { data.bsearch {|number| number > 40_000_000 } }
end
user system total real
find 3.020000 0.010000 3.030000 (3.028417)
bsearch 0.000000 0.000000 0.000000 (0.000006)
Як ви можете бачити, bsearch набагато швидше. Однак, існує досить велика хитрість, пов'язана з використанням bsearch: масив повинен бути відсортований. Це дещо обмежує його корисність, але цей метод все ще варто тримати в голові, для випадків, де це може стати в нагоді.
Flat_map
При роботі з реляційними даними, іноді нам потрібно зібрати в купу незв'язані атрибути і повернути їх у масив. Давайте уявимо, що ви мали на увазі блог, у якому хотіли знайти авторів коментарів, залишених під записом, написаним в минулому місяці, і ви маєте список цих користувачів.
Ви могли б зробити щось на зразок цього:
module CommentFinder
def self.find_for_users(user_ids)
users = User.where(id: user_ids)
user.posts.map do |post|
post.comments.map |comment|
comment.author.username
end
end
end
end
Ви отримаєте наступний результат:
[[['Ben', 'Sam', 'David'], ['Keith']], [[], [nil]], [['Chris'], []]]
Але ви просто хотіли отримати ім’я авторів! Я думаю, ми можемо викликати метод flatten.
module CommentFinder
def self.find_for_users(user_ids)
users = User.where(id: user_ids)
user.posts.map { |post|
post.comments.map { |comment|
comment.author.username
}.flatten
}.flatten
end
end
Інший варіант-використання flat_map.
module CommentFinder
def self.find_for_users(user_ids)
users = User.where(id: user_ids)
user.posts.flat_map { |post|
post.comments.flat_map { |comment|
comment.author.username
}
}
end
end
Особливої різниці немає, але це краще, ніж викликати flatten кілька разів.
Array.new
Одного разу, коли я був у bootcamp, наш учитель Джефф Казимир (засновник школи Тюрінга) попросив нас побудувати гру Морський бій за годину. Це було відмінна вправа в об'єктно-орієнтованому програмуванні. Нам потрібні були правила, гравці, ігри, і дошка.
Створення дошки - це весела справа. Після кількох ітерацій, я знайшов найпростіший спосіб налаштувати сітку 8x8:
class Board
def board
@board ||= Array.new(8) { Array.new(8) { '0' } }
end
end
Що тут відбувається? При виклику array.new з аргументом, він створює масив такої довжини:
Array.new(8)
#=> [nil, nil, nil, nil, nil, nil, nil, nil]
Коли ви передати блок, він заповнює масив елементами. Ми отримуємо наступне:
Array.new(8) { 'O' }
#=> ['O', 'O', 'O', 'O', 'O', 'O', 'O', 'O']
Так що, якщо ви передаєте масив з восьми елементів блоку, який створює масив з восьми елементів, в кінцевому підсумку ви отримаєте масив 8x8.
За допомогою array.new, Ви можете створити всі види масивів даних з підтримкою будь-якого рівня вкладеності.
<=>
Зореліт є одним з моїх улюблених конструкцій в Ruby. Він зустрічається в більшості вбудованих в Ruby класів, і це дійсно корисний інструмент при роботі з перераховуваними типами.
Щоб проілюструвати, як це працює, давайте розглянемо поведінку з Fixnums. Якщо ви викликаєте 5<=>5, то повертається 0. Якщо ви викликаєте 4<=>5, повертається -1, 5<=>4 - 1. В принципі, якщо два числа збігаються, то повертається 0, в іншому випадку вона повертає -1 для “від найменшого до найбільшого” сортування і 1 для зворотного способу.
Розглянемо приклад, який називається годинник, де вам доведеться відрегулювати час, використовуючи користувальницькі + і - методи. Згідно задачі, ви не можете додати більше ніж 60 хвилин, тому що в результаті отримаєте неприпустиме значення. Так що вам доведеться регулювати процес, збільшуючи час на годину і віднімаючи 60 хвилин.
def fix_minutes
until (0...60).member? minutes
@hours -= 60 <=> minutes
@minutes += 60 * (60 <=> minutes)
end
@hours %= 24
self
end
Я сподіваюся, що ці методи допоможуть вам розширити свій запас знань мови Ruby.
842 8

Коментарі:
0
max_si_m
цікаво подивитися на бенчмарк методу
#tap