On Fri, Dec 11, 2015, 8:20 PM Franklin? Lee firstname.lastname@example.org wrote:
By the way, there are other usecases for ignoring arguments for caching. For example, dynamic programming where the arguments are the indices of a sequence, or some other object (tree?) which isn't a recursive argument. I recommend that those also be done with a closure (separating the recursive part from the initial arguments), but I think it's worth considering an lru_cache implementation for students who haven't learned to, er, abuse closures. Unless someone thinks a recipe can/should be added to the docs.
This whole thing is probably best implemented as two separate functions rather than using a closure, depending on how intertwined the code paths are for the shortcut/non-shortcut versions.
@lru_cache def factorial(n): if n < 2: return 1 return n * factorial(n-1)
@lru_cache def factorial_faster(n, shortcut=None): if shortcut is not None: return shortcut return factorial(n)
On Fri, Dec 11, 2015 at 8:01 PM, Franklin? Lee email@example.com wrote:
- Rewrite your recursive function so that the partial state is a
nonlocal variable (in the closure), and memoize the recursive part.
I'd flip the rare-case to the except block and put the normal-case in the try block. I believe this will be more compute-efficient and more readable.
def factorial(n, answer=None): try: return factorial.recursive(n) except AttributeError: @lru_cache() def recursive(n): # shortcut if answer is not None: return answer # non-shortcut if n < 2: return 1 return n * recursive(n-1) factorial.recursive = recursive return recursive(n)
Note that the original question was how to handle an optional shortcut parameter that would not change the output but simply increase speed if (and only if) that call was a cache miss. A successive cache hit should be near instantaneous, regardless of the optional parameter.