Skip to content

Instantly share code, notes, and snippets.

@monorkin
Last active November 10, 2016 07:24
Show Gist options
  • Save monorkin/b4feb280d83685919694915959e605a7 to your computer and use it in GitHub Desktop.
Save monorkin/b4feb280d83685919694915959e605a7 to your computer and use it in GitHub Desktop.
require 'rspec/autorun'
module Memoizer
def memoize(method_name)
method = instance_method(method_name)
define_method(method_name) do |*args|
variables = instance_variables.each_with_object({}) do |name, hash|
hash[name] = instance_variable_get(name) unless name == :@memoization
end
@memoization ||= {}
@memoization[method_name] ||= {}
key = { args: args, variables: variables }.hash
@memoization[method_name][key] ||=
@memoization[method_name].fetch(key) { method.bind(self).call(*args) }
@memoization[method_name][key]
end
end
end
class Computer
extend Memoizer
attr_accessor :a
def initialize(a)
@a = a
end
memoize def very_complex_computation(b)
a + b + rand(1_000_000)
end
end
describe Computer do
describe '#very_complex_computation' do
let(:a) { 13 }
subject { Computer.new(a) }
it 'returns a number' do
expect(subject.very_complex_computation(7)).to be_a(Numeric)
end
end
end
describe Memoizer do
subject do
Class.new do
extend Memoizer
attr_accessor :base
def initialize(base)
@base = base
end
memoize def sum(additional)
sum = base + additional
puts(sum)
sum
end
memoize def diff(additional)
diff = base - additional
puts(diff)
diff
end
end
end
context 'with a single instance' do
describe '#memoize' do
let(:base) { 10 }
let(:instance) { subject.new(base) }
it 'memoizes the result of the method call' do
expect(instance).to receive(:puts)
expect(instance.sum(5)).to eq(15)
expect(instance.sum(5)).to eq(15)
end
it 'memoizes the result regarding the input' do
expect(instance).to receive(:puts).twice
expect(instance.sum(5)).to eq(15)
expect(instance.sum(15)).to eq(25)
end
it 'memoizes the result regarding the state' do
expect(instance).to receive(:puts).twice
expect(instance.sum(5)).to eq(15)
expect(instance.sum(5)).to eq(15)
instance.base = 20
expect(instance.sum(5)).to eq(25)
expect(instance.sum(5)).to eq(25)
end
it 'doesn\'t interfere with other memoized methods' do
expect(instance).to receive(:puts).twice
expect(instance.sum(5)).to eq(15)
expect(instance.sum(5)).to eq(15)
expect(instance.diff(5)).to eq(5)
expect(instance.diff(5)).to eq(5)
end
end
end
context 'with multiple instances' do
describe '#memoize' do
let(:base) { 10 }
let(:instance_1) { subject.new(base) }
let(:instance_2) { subject.new(base) }
it 'memoizes the result without interfeering with other instances' do
expect(instance_1).to receive(:puts)
expect(instance_1.sum(5)).to eq(15)
expect(instance_1.sum(5)).to eq(15)
expect(instance_2).to receive(:puts)
expect(instance_2.sum(5)).to eq(15)
expect(instance_2.sum(5)).to eq(15)
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment