Version 4 of Factorial Using Event Loop

Updated 2003-10-01 13:58:57

I'm easily amused (especially when amusing myself). See Tail call optimization for other variations.

 proc continue: {args} {
    after idle $args
 }

 proc fac {n result {accum 1}} {
    if {$n > 1} {
        continue: fac [expr {$n-1}] $result [expr {$accum * $n}]
    } else {
        upvar $result res
        set res $accum
    }
 }

 fac 10 ::foo
 vwait ::foo
 puts $::foo

Hey, and there is room for more things to be done while calculating...

 proc do_ntimes {cnt code} {
    uplevel $code
    if {$cnt > 0} {
        continue: do_ntimes [incr cnt -1] $code
    }
 }

 fac 10 ::foo
 do_ntimes 10 {puts "hello world"}
 vwait ::foo
 puts $::foo

-- Todd Coram


RS Interesting: this is using the event queue instead of the recursion stack (which has a fixed depth limit - does the event queue have a similar limit?). Functional programmers will of course abhor procedures which do not return a value, as they are run asynchronously, and so mostly global variables will have to be used. But still - another cool braintwister...

DKF: Functional programmers would be better off using a callback (function fragment!) to print the result value instead.


DKF: Here's another variant:

 proc calculateFactorial {ary target} {
    upvar #0 $ary a
    if {$target < 2} {
       set a($target) 1
       return
    }
    if {[info exist a($target)] && [string is integer $a($target)]} {
       set a($target) $a($target)
       return
    }
    set t1 [expr {$target-1}]
    set t2 [expr {$target-2}]
    if {![info exist a($t1)]} {
       set a($t1) pending
       after 1000 calculateFactorial $ary $t1
    }
    if {![info exist a($t2)]} {
       set a($t2) pending
       after 1000 calculateFactorial $ary $t2
    }
    if {![string is integer $a($t1)] || ![string is integer $a($t2)]} {
       after 1000 calculateFactorial $ary $target
       return
    }
    set a($target) [expr {$a($t1)+$a($t2)}]
 }

Enjoy!


Erlang programmers use a similiar idiom to the one I used at the top of this page. Erlang is (er mostly) functional (no side-effects), but it utilizes message passing. Factorial could be an Erlang "process" and the result is simply sent back to the requestor. This would be easy to do in Tcl (and you kinda get that with what I was doing with the event loop!).

-- Todd Coram [?]