Storing variables upon calling Higher Order Function

treebeard

if I want to call a function with an argument, without using anything fancy like declaring nonlocal variables or creating a list, how do I store my argument each time I call my function so that it 'remembers' the previous arguments that I have called previously? My idea is as follows:

# stac(x) is a function that returns another function (nested) or itself. 
>>> stac(4)(5)(6)(4)(6)(7)(8)(3)(0) 
# if 0, prints the previous numbers that was called most recently to first call 
3
8
7
.
.
4 

Some thing similar what I have in mind is detecting whether if there is a repeated argument in stac(x), such that every time it prints out x if x matched one of the previous arguments.

# stac(x) is a function that returns another function (nested) or itself. 
>>> stac(1)(2)(3)(4)(1)(3)(5)(7)
1
3
# since 1 and 3 is repeated

My thoughts on how to approach the idea is that we return a function defined in the current frame so that, if our argument in the new frame meets a condition (or not) it checks the previous frames down to the frame when we first call an argument. Though I am not sure if that works. If someone has an idea of how to implement it I would be grateful!

Mad Physicist

You need something that's accessible to the namespace of each of the objects you return, otherwise you won't be able to print all the prior arguments. So yeah, you'll probably want a list or deque or some other mutable container. But you can make it local to the first function call:

def stac1(n):
    s = []
    def k(n):
        if n == 0:
            print(*s, sep='\n')
            return s
        s.append(n)
        return k
    return k(n)

Your second example can be implemented in a similar manner but with a set instead of a sequence:

def stac2(n):
    s = set()
    def k(n):
        if n in s:
            print(n)
        else:
            s.add(n)
        return k
    return k(n)

While this technically uses a shared container, each call to stac instantiates its own, so there is no global shared state. At the same time, neither function will behave correctly if it's split:

>>> a = stac1(1)(2)
>>> a(3)(0)
1
2
3
[1, 2, 3]
>>> a(0)
1
2
3
[1, 2, 3]
# desired [1, 2]
>>> b = stac2(1)(2)
>>> b(5)(1)
1
>>> b(5)
5
# desired nothing

A naive approach would be to pass around an immutable sequence/frozenset, or at least a copy, e.g., as a second default argument to k. That's quite slow and wasteful though, at least for stac1. You could use a linked list instead, so each branch after the split got its own sequence without having to duplicate the parents:

from functools import partial

def stac1b(n):
    def k(p, n):
        if n:
            return partial(k, [n, p])
        s = []
        while p:
            n, p = p
            s.append(n)
        s.reverse()
        print(*s, sep='\n')
        return s
    return k(None, n)

stac2 is a little tougher to deal with. On the one hand, a linked list approach just like the one shown for stac1b is possible. The drawback is that a full linear search must be performed for each added element. On the other hand, passing around copies of sets seems wasteful, but makes the lookup much faster. Since duplicate elements don't require a copy, I'll show the latter option:

def stac2b(n):
    def k(s, n):
        if n in s:
            print(n)
            return partial(k, s)
        return partial(k, s | {n})
    return k(set(), n)

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related