On Fri, Dec 11, 2015, 8:20 PM Franklin? Lee <leewangzhong+python@gmail.com> 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
<leewangzhong+python@gmail.com> wrote:
> Solutions:
> 1. 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.