Module: MemoWise

Defined in:
lib/memo_wise.rb,
lib/memo_wise/version.rb,
lib/memo_wise/internal_api.rb

Overview

MemoWise is the wise choice for memoization in Ruby.

  • Q: What is memoization?
  • A: via Wikipedia:

     [Memoization is] an optimization technique used primarily to speed up
     computer programs by storing the results of expensive function
     calls and returning the cached result when the same inputs occur
     again.
    

To start using MemoWise in a class or module:

  1. Add prepend MemoWise to the top of the class or module
  2. Call MemoWise.memo_wise to implement memoization for a given method

See Also:

Defined Under Namespace

Classes: InternalAPI

Constant Summary collapse

VERSION =
"1.0.0"

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.memo_wise(method_name) ⇒ void

This method returns an undefined value.

Implements memoization for the given method name.

  • Q: What does it mean to "implement memoization"?
  • A: To wrap the original method such that, for any given set of arguments, the original method will be called at most once. The result of that call will be stored on the object. All future calls to the same method with the same set of arguments will then return that saved result.

Methods which implicitly or explicitly take block arguments cannot be memoized.

Examples:

class Example
  prepend MemoWise

  def method_to_memoize(x)
    @method_called_times = (@method_called_times || 0) + 1
  end
  memo_wise :method_to_memoize
end

ex = Example.new

ex.method_to_memoize("a") #=> 1
ex.method_to_memoize("a") #=> 1

ex.method_to_memoize("b") #=> 2
ex.method_to_memoize("b") #=> 2

Parameters:

  • method_name (Symbol)

    Name of method for which to implement memoization.


# File 'lib/memo_wise.rb', line 280


.preset_memo_wise(method_name, *args, **kwargs) ⇒ Object

Implementation of #preset_memo_wise for class methods.

Examples:

class Example
  prepend MemoWise

  def self.method_called_times
    @method_called_times
  end

  def self.method_to_preset
    @method_called_times = (@method_called_times || 0) + 1
    "A"
  end
  memo_wise self: :method_to_preset
end

Example.preset_memo_wise(:method_to_preset) { "B" }

Example.method_to_preset #=> "B"

Example.method_called_times #=> nil

# File 'lib/memo_wise.rb', line 318


.reset_memo_wise(method_name = nil, *args, **kwargs) ⇒ Object

Implementation of #reset_memo_wise for class methods.

Examples:

class Example
  prepend MemoWise

  def self.method_to_reset(x)
    @method_called_times = (@method_called_times || 0) + 1
  end
  memo_wise self: :method_to_reset
end

Example.method_to_reset("a") #=> 1
Example.method_to_reset("a") #=> 1
Example.method_to_reset("b") #=> 2
Example.method_to_reset("b") #=> 2

Example.reset_memo_wise(:method_to_reset, "a") # reset "method + args" mode

Example.method_to_reset("a") #=> 3
Example.method_to_reset("a") #=> 3
Example.method_to_reset("b") #=> 2
Example.method_to_reset("b") #=> 2

Example.reset_memo_wise(:method_to_reset) # reset "method" (any args) mode

Example.method_to_reset("a") #=> 4
Example.method_to_reset("b") #=> 5

Example.reset_memo_wise # reset "all methods" mode

# File 'lib/memo_wise.rb', line 344


Instance Method Details

#preset_memo_wise(method_name, *args, **kwargs) ⇒ void

This method returns an undefined value.

Presets the memoized result for the given method to the result of the given block.

This method is for situations where the caller already has the result of an expensive method call, and wants to preset that result as memoized for future calls. In other words, the memoized method will be called zero times rather than once.

NOTE: Currently, no attempt is made to validate that the given arguments are valid for the given method.

Examples:

class Example
  prepend MemoWise
  attr_reader :method_called_times

  def method_to_preset
    @method_called_times = (@method_called_times || 0) + 1
    "A"
  end
  memo_wise :method_to_preset
end

ex = Example.new

ex.preset_memo_wise(:method_to_preset) { "B" }

ex.method_to_preset #=> "B"

ex.method_called_times #=> nil

Parameters:

  • method_name (Symbol)

    Name of a method previously set up with #memo_wise.

  • args (Array)

    (Optional) If the method takes positional args, these are the values of position args for which the given block's result will be preset as the memoized result.

  • kwargs (Hash)

    (Optional) If the method takes keyword args, these are the keys and values of keyword args for which the given block's result will be preset as the memoized result.

Yield Returns:

  • (Object)

    The result of the given block will be preset as memoized for future calls to the given method.


430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
# File 'lib/memo_wise.rb', line 430

def preset_memo_wise(method_name, *args, **kwargs)
  unless block_given?
    raise ArgumentError,
          "Pass a block as the value to preset for #{method_name}, #{args}"
  end

  api = MemoWise::InternalAPI.new(self)
  api.validate_memo_wised!(method_name)

  if method(method_name).arity.zero?
    @_memo_wise[method_name] = yield
  else
    hash = @_memo_wise.fetch(method_name) do
      @_memo_wise[method_name] = {}
    end
    hash[api.fetch_key(method_name, *args, **kwargs)] = yield
  end
end

#reset_memo_wise(method_name = nil, *args, **kwargs) ⇒ void

This method returns an undefined value.

Resets memoized results of a given method, or all methods.

There are three reset modes depending on how this method is called:

method + args mode (most specific)

  • If given method_name and either args or kwargs or both:
  • Resets only the memoized result of calling method_name with those particular arguments.

method (any args) mode

  • If given method_name and neither args nor kwargs:
  • Resets all memoized results of calling method_name with any arguments.

all methods mode (most general)

  • If not given method_name:
  • Resets all memoized results of calling all methods.

Examples:

class Example
  prepend MemoWise

  def method_to_reset(x)
    @method_called_times = (@method_called_times || 0) + 1
  end
  memo_wise :method_to_reset
end

ex = Example.new

ex.method_to_reset("a") #=> 1
ex.method_to_reset("a") #=> 1
ex.method_to_reset("b") #=> 2
ex.method_to_reset("b") #=> 2

ex.reset_memo_wise(:method_to_reset, "a") # reset "method + args" mode

ex.method_to_reset("a") #=> 3
ex.method_to_reset("a") #=> 3
ex.method_to_reset("b") #=> 2
ex.method_to_reset("b") #=> 2

ex.reset_memo_wise(:method_to_reset) # reset "method" (any args) mode

ex.method_to_reset("a") #=> 4
ex.method_to_reset("b") #=> 5

ex.reset_memo_wise # reset "all methods" mode

Parameters:

  • method_name (Symbol, nil) (defaults to: nil)

    (Optional) Name of a method previously set up with #memo_wise. If not given, will reset all memoized results for all methods.

  • args (Array)

    (Optional) If the method takes positional args, these are the values of position args for which the memoized result will be reset.

  • kwargs (Hash)

    (Optional) If the method takes keyword args, these are the keys and values of keyword args for which the memoized result will be reset.


514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
# File 'lib/memo_wise.rb', line 514

def reset_memo_wise(method_name = nil, *args, **kwargs)
  if method_name.nil?
    unless args.empty?
      raise ArgumentError, "Provided args when method_name = nil"
    end

    unless kwargs.empty?
      raise ArgumentError, "Provided kwargs when method_name = nil"
    end

    return @_memo_wise.clear
  end

  unless method_name.is_a?(Symbol)
    raise ArgumentError, "#{method_name.inspect} must be a Symbol"
  end

  unless respond_to?(method_name, true)
    raise ArgumentError, "#{method_name} is not a defined method"
  end

  api = MemoWise::InternalAPI.new(self)
  api.validate_memo_wised!(method_name)

  if args.empty? && kwargs.empty?
    @_memo_wise.delete(method_name)
  else
    @_memo_wise[method_name]&.
      delete(api.fetch_key(method_name, *args, **kwargs))
  end
end