blob: 3b4c81f48b01502015c9d2909493f55cebc33898 [file] [log] [blame]
#!/usr/bin/python3 -i
#
# Copyright (c) 2019 Collabora, Ltd.
#
# SPDX-License-Identifier: Apache-2.0
#
# Author(s): Ryan Pavlik <ryan.pavlik@collabora.com>
"""RecursiveMemoize serves as a base class for a function modeled
as a dictionary computed on-the-fly."""
class RecursiveMemoize:
"""Base class for functions that are recursive.
Derive and implement `def compute(self, key):` to perform the computation:
you may use __getitem__ (aka self[otherkey]) to access the results for
another key. Each value will be computed at most once. Your
function should never return None, since it is used as a sentinel here.
"""
def __init__(self, func, key_iterable=None, permit_cycles=False):
"""Initialize data structures, and optionally compute/cache the answer
for all elements of an iterable.
If permit_cycles is False, then __getitem__ on something that's
currently being computed raises an exception.
If permit_cycles is True, then __getitem__ on something that's
currently being computed returns None.
"""
self._compute = func
self.permit_cycles = permit_cycles
self.d = {}
if key_iterable:
# If we were given an iterable, let's populate those.
for key in key_iterable:
_ = self[key]
def __getitem__(self, key):
"""Access the result of computing the function on the input.
Performed lazily and cached.
Implement `def compute(self, key):` with the actual function,
which will be called on demand."""
if key in self.d:
ret = self.d[key]
# Detect "we're computing this" sentinel and
# fail if cycles not permitted
if ret is None and not self.permit_cycles:
raise RuntimeError("Cycle detected when computing function: " +
"f({}) depends on itself".format(key))
# return the memoized value
# (which might be None if we're in a cycle that's permitted)
return ret
# Set sentinel for "we're computing this"
self.d[key] = None
# Delegate to function to actually compute
ret = self._compute(key)
# Memoize
self.d[key] = ret
return ret
def get_dict(self):
"""Return the dictionary where memoized results are stored.
DO NOT MODIFY!"""
return self.d