Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does adding the decorator @lru_cache(from functools) break this function?

The function is a part of the solution to the following problem:

"Find all valid combinations of k numbers that sum up to n such that the following conditions are true:

Only numbers 1 through 9 are used. Each number is used at most once.

Return a list of all possible valid combinations. The list must not contain the same combination twice, and the combinations may be returned in any order."

An example test case which fails with the code:

import functools

@functools.lru_cache(maxsize=None)
def CombSum(k,n,num):
    if k<=0 or n<=0 or num>9:
        return []
    if k==1 and n>=num and n<=9:
        return [[n]]
    ans=[]
    while num <= 9:
        for arr in CombSum(k-1,n-num,num+1):
            arr.append(num)
            ans.append(arr)
        num+=1
    return ans
print(CombSum(4,16,1))

produces

[[9, 4, 2, 1], [8, 5, 2, 1], [7, 6, 2, 1], [8, 4, 3, 1], [7, 5, 3, 1], [6, 5, 4, 1, 5, 3, 2], [7, 4, 3, 2], [6, 5, 4, 1, 5, 3, 2]]

The above result is incorrect and when I remove the decorator below, everything works fine:

import functools

def CombSum(k,n,num):
    if k<=0 or n<=0 or num>9:
        return []
    if k==1 and n>=num and n<=9:
        return [[n]]
    ans=[]
    while num <= 9:
        for arr in CombSum(k-1,n-num,num+1):
            arr.append(num)
            ans.append(arr)
        num+=1
    return ans
print(CombSum(4,16,1))

produces

[[9, 4, 2, 1], [8, 5, 2, 1], [7, 6, 2, 1], [8, 4, 3, 1], [7, 5, 3, 1], [6, 5, 4, 1], [7, 4, 3, 2], [6, 5, 3, 2]]

This is the correct answer.

Why is the decorator breaking the function?

like image 623
A-ar Avatar asked Jun 28 '26 14:06

A-ar


2 Answers

You violated the decorator’s contract. You’re mutating elements of the returned list. The decorator should only be applied to Pure functions.

You defined a Public API that returns a list of lists. Switch to returning an immutable tuple of tuples. Then your approach will be compatible with the LRU decorator.

like image 163
J_H Avatar answered Jun 30 '26 04:06

J_H


Use a immutable object such as a tuple and it will work

@functools.lru_cache(maxsize=None)
def CombSum2(k,n,num):
    if k<=0 or n<=0 or num>9:
        return tuple()
    if k==1 and n>=num and n<=9:
        return (n,),
    ans = tuple()
    while num <= 9:
        for arr in CombSum2(k-1,n-num,num+1):
            arr += num,
            ans += arr,
        num+=1
    return ans
like image 29
cards Avatar answered Jun 30 '26 04:06

cards