Open ez2rok opened 2 years ago
The stack-overflow post Memoization recipe that allows non-hashable arguments recommends implementing a custom hash function that uses an object's __repr__
for hashing.
However, this is inefficient and still poses various issues. Several commenters on this post point out that even with hashing an object's `repr' you're not guaranteed that the hash is valid, since a dictionary of sets (for example) could still print the values in an arbitrary order.
Github user @slowli creates a gist which tries to turn non-hashable arguments into hashable arguments with the lines:
mapargs = lambda *args: args
def cached_fn(*args):
mapped_args = mapargs(*args)
if not mapped_args in cache:
cache[mapped_args] = fn(*args)
return cache[mapped_args]
Yet all mapargs
does is cast args to a tuple which still is not helpful if the values in the tuple are unhashable, e.g. lists. I even tried implementing this and like I thought, it did not resolve the error.
I'm thinking of just writing some code to automatically cast lists to tuples in RecursionVisualizer
. This has several advantages:
RecursionVisualizer
is likely to encounter, the values we change are likely going to be pointers or integer values. The lists that we recursively iterate through don't usually change. So I don't think creating an immutable tuple will ever affect the performance of 99% of recursive functions used with RecursionVisualizer
.
Goal
Behind the scenes, 'RecursionVisualizer' works by using a dictionary to cache the arguments seen in the recursive function. This relies upon the arguments of the recursive function being hashable. However, sometimes these arguments are lists which aren't hashable.
My goal is to have 'RecursionVisualizer' work for all recursive functions, even if they have non-hashable arguments such as lists.
The Issue
For example, consider this implementation of the 0-1 knapsack problem:
which could be called by writing
With the current
RecursionVisualizer
version 0.0.0 implementation, this outputs the errorThe last line of this error message says:
TypeError: unhashable type: 'list'
because theknapsack
function had the argumentsweights
andvalues
which are both unhashable lists.What I've Tried
I currently have two workarounds for this: 1) Simply make all data structures hashable. In this case, I could cast
weights
andvalues
to tuples and everything would work fine:results in no errors. 2) Another option is that I could rewrite
knapsack
to have a wrapper function with the unhashableweights
andvalues
as it arguments and an internal function that simply has the indexi
andcapacity
as its arguments. This internal function is where the recursion is actually performed and because it is within the same scope of the outer wrapper function, it still has access to theweights
andvalues
lists without actually having these unhashable lists as arguments to its function.This code works without any issues.
Moving Forward
Although these are both valid workarounds, I'm still looking for a more general purpose way to get around this problem.