70 lines
2.4 KiB
Python
70 lines
2.4 KiB
Python
#!/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
|