Task

Create a list of ten functions, in the simplest manner possible   (anonymous functions are encouraged),   such that the function at index    i     (you may choose to start     i     from either    0    or    1),    when run, should return the square of the index,   that is,     i  2.

Display the result of running any but the last function, to demonstrate that the function indeed remembers its value.



Goal

Demonstrate how to create a series of independent closures based on the same template but maintain separate copies of the variable closed over.

In imperative languages, one would generally use a loop with a mutable counter variable.

For each function to maintain the correct number, it has to capture the value of the variable at the time it was created, rather than just a reference to the variable, which would have a different value by the time the function was run.

See also: Multiple distinct objects

package require Tcl 8.6; # Just for tailcall command
# Builds a value-capturing closure; does NOT couple variables
proc closure {script} {
    set valuemap {}
    foreach v [uplevel 1 {info vars}] {
	lappend valuemap [list $v [uplevel 1 [list set $v]]]
    }
    set body [list $valuemap $script [uplevel 1 {namespace current}]]
    # Wrap, to stop untoward argument passing
    return [list apply [list {} [list tailcall apply $body]]]
    # A version of the previous line compatible with Tcl 8.5 would be this
    # code, but the closure generated is more fragile:
    ### return [list apply $body]
}
 
# Simple helper, to avoid capturing unwanted variable
proc collectFor {var from to body} {
    upvar 1 $var v
    set result {}
    for {set v $from} {$v < $to} {incr v} {lappend result [uplevel 1 $body]}
    return $result
}
# Build a list of closures
proc buildList {} {
    collectFor i 0 10 {
	closure {
	    # This is the body of the closure
	    return [expr $i*$i]
	}
    }
}
set theClosures [buildList]
foreach i {a b c d e} {# Do 5 times; demonstrates no variable leakage
    set idx [expr {int(rand()*9)}]; # pick random int from [0..9)
    puts $idx=>[{*}[lindex $theClosures $idx]]
}