calcOnlyOnce

This really simple procedure is intended for long-running calculations:

array set delayed {}
proc calcOnlyOnce args {
    set args1 [concat [
        uplevel 1 namespace origin [lindex $args 0]] [lrange $args 1 end]]
    variable delayed
    if {[info exists delayed($args1)]} then {
        set delayed($args1)
    } else {
        set delayed($args1) [uplevel 1 $args]
    }
}

Example usage -- this proc is slow:

proc fib n {
    if {$n <= 1} then {
        return 1
    } else {
        set n1 [expr {$n-1}]
        set n2 [expr {$n-2}]
        expr {[fib $n1] + [fib $n2]}
    }
}

Now test:

 % time {calcOnlyOnce fib 25} 1
 246325 microseconds per iteration
 % time {calcOnlyOnce fib 25} 1
 39 microseconds per iteration
 %  

The second call on same procedure was faster due to the fact that it was not re-calculated but "remembered" instead.


AM 2007-08-17: This is known as "memoization" - there are several Wiki pages on the subject.

AMG: Here's a link: memoizing

wdb: Thanks for the hint ... before writing this page, I searched "delayed", "freeze" etc. Memoizing ... ok, I've learned a new word!