П'ять методів Ruby, які вам слід використовувати

18 січня 2015 19:43 comandante 842 1

Є щось магічне в 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

Схожі матеріали:

Коментарі:

max_si_m

15 Сер 2015 22:54

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

Авторизуйтесь, щоб залишити коментар.