See also [Accumulator Generators]. ---- The Accumulator Generator is a problem used by Paul Graham to compare the relative power of different programming languages. ***The Problem*** We want to write a function that generates accumulators-- a function that takes a number n, and returns a function that takes another number i and returns n incremented by i. (That's incremented by, not plus. An accumulator has to accumulate.) The original essay: http://www.paulgraham.com/icad.html The implementation in other languages: http://www.paulgraham.com/accgen.html [pmarin](2009-12-18). The Rules (from http://paulgraham.com/accgensub.html) 1. Takes, and returns functions that take, exactly one argument. 2. Works for any numeric type-- i.e. can take both ints and floats and returns functions that can take both ints and floats. (It is not enough simply to convert all input to floats. An accumulator that has only seen integers must return integers.) 3. Generates functions that return the sum of every number ever passed to them, not just the most recent. 4. Returns a real function, meaning something that you can use wherever you could use a function you had defined in the ordinary way in the text of your program. 5. Doesn't store the accumulated value or the returned functions in a way that could cause them to be inadvertently modified by other code. My ([pmarin]) implementation: ====== package require TclOO namespace import oo::* proc foo n { set obj [object new] objdefine $obj method init {_n} {my variable n; set n $_n} objdefine $obj method acc i {my variable n; set n [expr {$n + $i}]} $obj init $n list ::apply [list i [subst -novariables {[set obj] acc $i}]] } proc destroy_acc acc { [lindex $acc 1 1 0] destroy } ====== A test: ====== > set acc [foo 3] > {*}$acc 1 4 > {*}$acc 10 14 > {*}$acc 0 14 > destroy_acc $acc > {*}$acc 1 invalid command name "::oo::Obj ====== ***Comments*** I'm not happy with my implementation, It works but It is still too vebose compared with other languages. I'm not sure if garbage collection is obligatory to solve this problem. Feel free to insert here your own better/clever approach. ---- [WHD]: How about this? ====== proc mkag {name n} { set ::${name} $n proc ::${name} {i} [list incr ::${name} \$i] } ====== [pmarin]: Sorry, I don't understand your code, Can you make a similar test like the above? Note that the accumulator must work with integer and floats. [AMG]: When WHD's mkag procedure is invoked, it (1) sets a global variable and (2) creates a procedure which increments that same global variable and returns its updated value. The global variable and the created procedure are given the same name, determined by the first argument to mkag. The second argument determines the increment step. Because the procedure created by mkag uses [incr], it only works with integers, not floats. To make it work with either, create an alternative to incr: ====== proc incr {varname {increment 1}} { upvar 1 $varname var set var [expr {$var + $increment}] } ====== ---- [DKF]: With 8.6, you can do this using [coroutine]s. ====== proc accumulator {name {n 0}} { coroutine $name apply {n { set i [yield [info coroutine]] while 1 { catch {set n [expr {$n + $i}]} set i [yield $n] } }} $n } ====== Testing... ====== > set acc [accumulator foo 3] ::foo > $acc 1 4 > $acc 10 14 > $acc 0 14 ====== The only real issue with this code is that it doesn't give good error messages when misused. ---- [slebetman]: Here's one that is portable up to when [[info body]] was introduced: way before Tcl 7.0 I suspect. Using self-modifying code (mechanics and implementation is ugly but the fact that it's doable is beautiful): ====== proc accumulator {name n} { proc $name {i} "proc $name {i} \[info body $name\]+\$i;expr \$i+$n" } ====== Usage... ====== % accumulator acc 3 % acc 1 4 % acc 10 14 % acc 0 14 ====== The reason I say the mechanics is ugly is because at this point: ====== % info body acc proc acc {i} [info body acc]+$i;expr $i+3+1+10+0 ====== the proc body keeps on growing... ---- [NEM] suggests: ====== proc accum {name n} { interp alias {} $name {} incr $name } ====== [slebetman]: I think that should be: ====== proc accum {name n} { interp alias {} $name {} incr ::$name set ::$name $n } ====== but it still doesn't work properly (as specified by the essay): ====== % accum x 10 10 % x 4 14 % x 3.7 expected integer but got "3.7" ====== ---- [AMG]: Here's one written using [sproc]: ====== sproc accumulator {{increment 1}} {value 0} { set value [expr {$value + $increment}] } set p [accumulator] $p ;# returns 1 $p ;# returns 2 $p 0.5 ;# returns 2.5 set q [accumulator value 20] $q ;# returns 21 $q ;# returns 22 $q 0.5 ;# returns 22.5 ====== As far as I can tell, the only one of Paul Graham's rules that it breaks is the first one, for two reasons. One, the increment argument is optional (defaults to 1). To be strict, replace "{{increment 1}}" with simply "increment". You can write it "{increment}" if you want. Two, the initial value argument is both optional and must be prefixed by the word "value". This can be easily resolved by making a wrapper procedure. ====== sproc Accumulator {increment} {value 0} { set value [expr {$value + $increment}] } proc accumulator {value} { Accumulator value $value } set p [accumulator] ;# wrong # args: should be "accumulator value" set q [accumulator 20] $q ;# wrong # args: should be "::::Accumulator0 increment" $q 0.5 ;# returns 20.5 ====== Now it's compliant. :^) <>Example