Skip to content

Instantly share code, notes, and snippets.

@jfarmer
Last active June 25, 2024 22:44
Show Gist options
  • Save jfarmer/b2d24820140378d330f2cc7d1334bd8e to your computer and use it in GitHub Desktop.
Save jfarmer/b2d24820140378d330f2cc7d1334bd8e to your computer and use it in GitHub Desktop.

And here's an argument for why you want your non-varying arguments to come first in your function signature.

Many languages have the concept of "partial application", where supplying a function with fewer arguments than it expects doesn't raise an error, but instead returns a function that is waiting for the remaining arguments.

For example, imagine if you had a function add(x,y) and add(1) didn't throw an error, but returned a function that was waiting for y and returned 1 + y when you supplied it with that single argument, e.g.,

def add(x, y):
    return x + y

add_one = add(1)  # Error!
add_one(10) == 11

Some languages have this behavior built-in, e.g., Haskell and F#, while others have ways of supporting it, e.g., Python via functools.partial (https://docs.python.org/3/library/functools.html#functools.partial)

With that we can make the above code work:

import functools

def add(x, y):
  return x + y

add_one = functools.partial(add, 1)
add_one(10) == 11  # Actually works!

What does this have to do with memoization? Well, sometimes we don't want to memoize every argument, as in the arrays of weights and values in the 01 Knapsack problem.

We can "freeze" the weights + values and memoize the remainder, though.

import functools

def knapsack01(values, weights, num_items, capacity):
    pass

values = [...]
weights =  [...]
knapsack01_memo = memoize(functools.partial(knapsack01, values, weights))

Now calling knapsack01_memo(num_items, 100) (for example) only uses those arguments as keys in the memo cache.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment