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:
- Add
prepend MemoWise
to the top of the class or module - Call MemoWise.memo_wise to implement memoization for a given method
See Also:
- MemoWise.memo_wise for API and usage examples.
- README for general project information.
Defined Under Namespace
Classes: InternalAPI
Constant Summary collapse
- VERSION =
"1.11.0"
Class Method Summary collapse
-
.memo_wise(method_name) ⇒ void
Implements memoization for the given method name.
-
.preset_memo_wise(method_name, *args, **kwargs) ⇒ Object
Implementation of #preset_memo_wise for class methods.
-
.reset_memo_wise(method_name = nil, *args, **kwargs) ⇒ Object
Implementation of #reset_memo_wise for class methods.
Instance Method Summary collapse
-
#preset_memo_wise(method_name, *args, **kwargs) ⇒ void
Presets the memoized result for the given method to the result of the given block.
-
#reset_memo_wise(method_name = nil, *args, **kwargs) ⇒ void
Resets memoized results of a given method, or all methods.
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.
|
# File 'lib/memo_wise.rb', line 307
|
.preset_memo_wise(method_name, *args, **kwargs) ⇒ Object
Implementation of #preset_memo_wise for class methods.
|
# File 'lib/memo_wise.rb', line 345
|
.reset_memo_wise(method_name = nil, *args, **kwargs) ⇒ Object
Implementation of #reset_memo_wise for class methods.
|
# File 'lib/memo_wise.rb', line 371
|
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.
455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 |
# File 'lib/memo_wise.rb', line 455 def preset_memo_wise(method_name, *args, **kwargs) raise ArgumentError, "#{method_name.inspect} must be a Symbol" unless method_name.is_a?(Symbol) raise ArgumentError, "Pass a block as the value to preset for #{method_name}, #{args}" unless block_given? MemoWise::InternalAPI.validate_memo_wised!(self, method_name) method = method(MemoWise::InternalAPI.original_memo_wised_name(method_name)) method_arguments = MemoWise::InternalAPI.method_arguments(method) if method_arguments == MemoWise::InternalAPI::NONE @_memo_wise[method_name] = yield return end hash = (@_memo_wise[method_name] ||= {}) case method_arguments when MemoWise::InternalAPI::ONE_REQUIRED_POSITIONAL then hash[args.first] = yield when MemoWise::InternalAPI::ONE_REQUIRED_KEYWORD then hash[kwargs.first.last] = yield when MemoWise::InternalAPI::SPLAT then hash[args] = yield when MemoWise::InternalAPI::DOUBLE_SPLAT then hash[kwargs] = yield when MemoWise::InternalAPI::MULTIPLE_REQUIRED n_parameters = method.parameters.size method.parameters.each_with_index do |(type, name), index| val = type == :req ? args[index] : kwargs[name] # Walk through the layers of nested hashes. When we get to the final # layer, yield to the block to set its value. if index < n_parameters - 1 hash = (hash[val] ||= {}) else hash[val] = yield end end else # MemoWise::InternalAPI::SPLAT_AND_DOUBLE_SPLAT # When we have both *args and **kwargs, we store the memoized values like: # { method_name: { args => { kwargs => memoized_value } } } # so we need to initialize `hash[args]`` if it does not already exist. (hash[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 eitherargs
orkwargs
or both: - Resets only the memoized result of calling
method_name
with those particular arguments.
method (any args) mode
- If given
method_name
and neitherargs
norkwargs
: - 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.
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 |
# File 'lib/memo_wise.rb', line 562 def reset_memo_wise(method_name = nil, *args, **kwargs) if method_name.nil? raise ArgumentError, "Provided args when method_name = nil" unless args.empty? raise ArgumentError, "Provided kwargs when method_name = nil" unless kwargs.empty? @_memo_wise.clear return end raise ArgumentError, "#{method_name.inspect} must be a Symbol" unless method_name.is_a?(Symbol) raise ArgumentError, "#{method_name} is not a defined method" unless respond_to?(method_name, true) MemoWise::InternalAPI.validate_memo_wised!(self, method_name) method = method(MemoWise::InternalAPI.original_memo_wised_name(method_name)) method_arguments = MemoWise::InternalAPI.method_arguments(method) # method_name == MemoWise::InternalAPI::NONE will be covered by this case. @_memo_wise.delete(method_name) if args.empty? && kwargs.empty? method_hash = @_memo_wise[method_name] case method_arguments when MemoWise::InternalAPI::ONE_REQUIRED_POSITIONAL then method_hash&.delete(args.first) when MemoWise::InternalAPI::ONE_REQUIRED_KEYWORD then method_hash&.delete(kwargs.first.last) when MemoWise::InternalAPI::SPLAT then method_hash&.delete(args) when MemoWise::InternalAPI::DOUBLE_SPLAT then method_hash&.delete(kwargs) when MemoWise::InternalAPI::SPLAT_AND_DOUBLE_SPLAT # Here, memoized values are stored like: # { method_name: { args => { kwargs => memoized_value } } } # so we need to delete the innermost value (because the same args array # may have multiple memoized values for different kwargs hashes). method_hash&.[](args)&.delete(kwargs) else # MemoWise::InternalAPI::MULTIPLE_REQUIRED n_parameters = method.parameters.size method.parameters.each_with_index do |(type, name), index| val = type == :req ? args[index] : kwargs[name] # Walk through the layers of nested hashes. When we get to the final # layer, delete its value. We use the safe navigation operator to # gracefully handle any layer not yet existing. if index < n_parameters - 1 method_hash = method_hash&.[](val) else method_hash&.delete(val) end end end end |