Updated 2015-06-13 08:48:25 by pooryorick

lrepeat was defined in TIP 136 [1], which has been approved for incorporation into Tcl 8.5.0 .

The doc is here: http://www.tcl.tk/man/tcl8.5/TclCmd/lrepeat.htm
lrepeat number element1 ?element2 element3 ...?

This command builds up a list by repeating elements. It is a part of Tcl 8.5.0 .

An example of how it works is
 lrepeat 3 a => a a a
 lrepeat 3 [lrepeat 3 0] => {0 0 0} {0 0 0} {0 0 0}

RS: Note the possible confusion with string repeat's syntax:
 % string repeat a 5

FW: A forward-compatible version for versions before 8.5 is available in Additional list functions. Here's another (RS 2005-08-02):
 proc lrepeat {n args} {if $n {eval concat $args [lrepeat [incr n -1] $args]}}

MG notes that this one doesn't seem to work quite the same as the 8.5 lrepeat, but the one on Additional list functions does - the one above doesn't group properly, for instance:
  % proc lrepeat {n args} {if $n {eval concat $args [lrepeat [incr n -1] $args]}}
  % lrepeat 3 [lrepeat 3 0]
  0 0 0 0 0 0 0 0 0

as opposed to the example at the top of the page:
  {0 0 0} {0 0 0} {0 0 0}

LES How is [lrepeat 3 a] different from [string repeat "a " 3]?

And how is lrepeat 3 [lrepeat 3 0] different from string repeat [list [string repeat "0 " 3]] 3?

RS The first creates a list, while the second creates a string that can be parsed into a list. For short lists, this doesn't matter much, but I noticed extra time costs of about half a second when converting a 2000x2500 list-like string to a nested list.

LES Half a second? In the entire operation or each nested list? Mentioning once again that I am completely ignorant of C, I wonder if it wouldn't have been possible to rewrite string repeat and optimize it, using whatever method lrepeat uses to do its trick. This lrepeat command still strikes me as something a bit superfluous. I mean, something rather more suitable to the Bag of algorithms than the core.

Lars H: Much of the extra time RS mentions is probably spent on parsing the string into lists and allocating memory to hold the necessary data structures. Note that the string representation of a 2000x2500 elements nested list is at least 10MB (1 list element char + 1 separating space per list element; 2000*2500*2). The total memory required for the parsed nested list is at least one order of magnitude larger (each list element will use up a Tcl_Obj, which costs at least 24 bytes each). This is what you get when parsing the list from a string.

The situation using lrepeat can be much better, thanks to that repeated elements can share the same Tcl_Obj. The result of
  lrepeat 2000 [lrepeat 2500 a]

can be encoded using only three Tcl_Objs: First one for the "a". Second, a list Tcl_Obj that is the result of [lrepeat 2500 a] (which holds 2500 references to the "a" object). Third, another list Tcl_Obj for the result of the outer lrepeat (which in turn holds 2000 references to the result of the inner lrepeat). But it is true that this would be possible even for a Tcl implementation of lrepeat.

RS In fact, my application was rotating binary photo images by 90 degrees, where I had to create a target list of lists into which to lset the pixels - the speed gain came when I replaced a string repeat construct with nested for loops, not having lrepeat yet in 8.4.

LES Does anyone realize that one requires number - string while the other requires string - number, and that is an excellent method to prevent people from memorizing correct syntax? - RS pointed this out just 11 lines above :^) LES I thought you were just pointing out that one adds spaces in-between (list) and the other concatenates it all together (string).

(unknown user at on 2005-12-16 16:44:16 said)

A potentially useful performance enhancement extension to lrepeat's syntax would be to allow [lrepeat $NUM] (which is currently a syntax error) to mean to return an empty list which is _internally_ preallocated to have space for $NUM list elements. Where this would be useful is where you are about to build a list and you know its final length beforehand:
 set lst [lrepeat $N] ;# functionally equivalent to: set lst {}
 for {set i 0} {$i < $N} {incr i} {
   lappend lst [funky $i] ;# won't trigger a list realloc

I accept that exposing internal memory handling to the script level is not really a Tcl-y thing to do; the closest i can think of being [fconfigure $fh -buffersize ...] and that's not really the same thing. Still, the syntax makes sense (to me, at least!) and i could see this functionality being useful in far more cases than the basic case of initialising a list with a single/group of elements.

AMG: Your suggestion isn't a good fit at all. For one thing, [llength [lrepeat $N {*}$args]] is defined to be equal to [expr {$N * [llength $args]}]. You're suggesting establishing a special case that more or less lives outside EIAS in the name of a performance enhancement that's already available. If you want to preallocate, simply do:
set lst [lrepeat $N {}]
for {set i 0} {$i < $N} {incr i} {
    lset lst [funky $i]

Jasp lrepeat on Tcl 8.5a4 doesn't seem to let you specify a repeat count of 0. Attempting to will return an error (must have a count of at least 1). There's situations where specifying 0 repeats might be useful (a count returned from an expr, for example). I'm surprised it's not already possible, and it would be nice for it to be fixed.

schlenk the TIP states 'positive integer', but non-negative integer would probably be better. Post a message to the Tclcore mailing list to discuss this, maybe a simple bug report is enough and no new or changed TIP is needed.

AMG: lrepeat's non-support of zero counts has been a big problem for me, often forcing me to wrap it with ifs and procs. I'm curious about the rationale for not supporting zero. There must have been a reason...

DGP See Tcl RFE 1671951 [2]

DKF: I see no reason in principle for not supporting a count of zero.

AMG: Fixed in TIP #323: Do Nothing Gracefully.