Memoization (мемоізація) — це техніка оптимізації, яка передбачає кешування результатів виконання функції, щоб уникнути повторних обчислень при наступних викликах. Це особливо корисно для дорогих операцій (наприклад, запитів до бази даних, складних обчислень або роботи з API).
Чому така назва?
Термін
"memoization" походить від англійського слова
"memo" (замітка, пам’ятка) та латинського кореня
"memor-" (пам’ятати). Це натякає на те, що функція "запам’ятовує" (кешує) свої результати, щоб уникнути повторних обчислень.
Вперше цей термін використав Дональд Мічі (Donald Michie), британський вчений у галузі штучного інтелекту, ще в
1968 році. Він описав техніку, коли обчислені значення зберігаються для подальшого використання.
Чому не просто "кешування"?
Memoization — це специфічний вид кешування.
-
Memoization зазвичай кешує результати функцій на рівні одного екземпляра об'єкта або в межах одного процесу.
-
Кешування загалом може включати збереження в базі даних, файлах, Redis, а не лише у змінних.
Тобто
вся мемоізація є кешуванням, але не все кешування — це мемоізація.
Як працює Memoization в Ruby?
У Ruby для мемоізації зазвичай використовують оператор ||= (або явне збереження значення в змінну), щоб зберігати результат у змінній і повертати його при наступних викликах.
class User
attr_reader :name
def initialize(name)
@name = name
end
def formatted_name
@formatted_name ||= name.upcase
end
end
user = User.new("Марті")
puts user.formatted_name # Виконає обчислення та збереже значення
puts user.formatted_name # Використає кешоване значення
У цьому випадку @formatted_name ||= name.upcase означає:
- Якщо @formatted_name ще не встановлений (nil або false), він отримає значення name.upcase.
- Інакше повертається вже збережене значення.
Оператор ||= працює коректно, якщо значення може бути nil або false, але якщо очікуване значення може бути false, тоді така мемоізація не спрацює. Наприклад:
@result ||= false
Якщо @result дорівнює false, вираз знову виконає обчислення.
Для надійної мемоізації можна використовувати явне присвоєння:
@result = some_expensive_operation if @result.nil?
Як працює Memoization в Rails?
У Ruby on Rails мемоізація використовується для оптимізації запитів до бази даних та інших витратних операцій. Приклад мемоізації у моделі:
class User < ApplicationRecord
def expensive_query
@expensive_query ||= some_heavy_calculation
end
private
def some_heavy_calculation
sleep(2) # Симуляція важкої операції
"Результат обчислення"
end
end
user = User.first
puts user.expensive_query # Чекаємо 2 секунди
puts user.expensive_query # Отримуємо кешоване значення
Memoization у before_action у контролері:
class UsersController < ApplicationController
before_action :set_user
def show
render json: { user: @user }
end
private
def set_user
@user ||= User.find(params[:id])
end
end
Це допомагає уникнути повторних запитів до бази даних, якщо метод @user використовується кілька разів у дії.
Чим відрізняється мемоізація від кешування?
Якщо коротко:
Мемоізація – це збереження результату в межах одного запущеного процесу (наприклад, у змінній екземпляра об’єкта).
Кешування – це ширший термін, який може включати збереження даних між процесами, у базах даних, файлах, Redis, Memcached тощо.