Recursive functions with run()?

Hello. I’m trying to use a recursive function with a run() function and a delay.

index = 0
def insatsu():
    global index, delayed_print
    print("count: " + str(index))
    index += 1    
    if index >= 3:
        finished()
    else:
        run(delayed_print, delayFrames = 60)

def finished():
    print('I am done')

delayed_print = "insatsu()"
run(delayed_print, delayFrames = 60)

When I run this code, I get:

count: 0
python >>>
Traceback (most recent call last):
File “Textport:insatsu()”, line 1
NameError: name ‘insatsu’ is not defined
python >>>

Can someone tell me why?

little trick that I like to use alot:

def my_function():
    pass
run( "args[0]()", my_function, delayFrames = 60 )

You can even extend this with a lambda function+

run ("args[0]()", lambda: my_function(custom_value), delayFrames = 60)

But just thinking you might be better of using something like a timer to do that as you give up control over the script with no easy way to stop it!

The specific problem you encounter is by run not executing from the module that you call run from, but instead being executed in the root (or td (?) ) module.
This means that it has no idea about insatsu.
you can try

run( "me.mod.insatsu()", fromOP = me, delayFrames = 60 )

but I use the method of passing the function object much better.,

Curious, can you use the Timer CHOP for this?

Hi @alphamoonbase I ended up not needing this code, whence the delayed response. Now that I needed it I tried the “me.mod.function()” way and it didn’t work. Your run(“args0”,… way did work though, so thank you! I don’t really understand how it works, though. What is the arg[0] an argument of? And if I need to actually pass arguments into the function would it be like this?:

run( "args[0]()", my_function, m_argument, delayFrames = 60 )

@greg I’d rather be able to do as much by script as possible for simple iterative processes, though I would’ve tried hooking up a Timer Chop if I were still stuck!

It is a little tricky but good to understand what is going on here.
First, it is important to understand that functions in python are also objects.
If you define a function like this:

def my_function():
    return 2

You can of course directly call this function using my_function() , but you can also store a refference to it for later use

my_var = my_function
[...]
my_var()

This btw also means you can store functions in to dictionaries and lists, which is also interresting.

So what are we doing with the run part? Checking that docs, we can see that the run-method is defined as follows: run(script, arg1, arg2.. so, we first pass a script (as a string(!) ), and then a arbitrary number of arguments that we can access via the args list in our script.
run("print( args )", "Foo", "Bar") Will print [“Foo”, “Bar”].
So, simple example would be to pass the refference to an operator, parameter or similiar. For example. lets say we want to destroy an operator after 3 frames.Probably first way of doing that is by passing the string as a arguments:
run( "op('args[0]').destroy()", "path/to/op" ) → args
But we could also pass the complete operator-object
run( "args[0].destroy()", op("path/to/op") )
Well, now I will tell you something: The destroy method is specific to that operator. So you can save a refference to that method, and call it later!(You can check this easily by running a little script print(op("operator1").destroy == op("operator2").destroy ) → False
So, lets instead just pass the destroy function as an argument and execute the function that got passed.
run( "args[0]()", op("path/to/op").destroy )

But as you said, there are situations when you have to pass specific information or call a function with specific arguments in a run method. Well, what if I tell you, that you can create a function in a function?

def target_function(argument1, argument2):
    argument1.so_domething()
    [...]

def my_function():
    target_operator = op("foo")
    def my_sub_function():
        target_function( target_operator, target_operator.par.Bar.eval() )
    run ("args[0]()", my_sub_function )

The my_sub_function will be a new one every time and hold the valued you assigned to the parameters until called. Extremly nifty. But you can also define a function in one line, a so called lambda by writing

def my_function():
    target_operator = op("foo")    
    run ("args[0]()", lambda :  target_function( target_operator, target_operator.par.Bar.eval() ) )

The concept is called closures and incredible powerfull! If you want to know more check this out for example.

If you ever come in to the situation of using javascript, this concept is all over the place there!

2 Likes