Riddle Me This, CFLOOP

Take this simple loop:

<cfloop from="1" to="10" index="i">
  #i#<br />
  <cfif i EQ 3>
    <cfset i += 3 />
  </cfif>
</cfloop>

What is the output?  My answer is the numbers 1-3 followed by 7-10.  But I was dismayed to learn this evening that it doesn't seem to be the case on my server.  There it outputs 1-10 with no breaks.  Can someone please confirm that this is NOT normal behaviour?  I'm running CF 8,0,1,195765 on x64 CentOS 5 (kernel 2.6.18-128.1.6.el5) with 64-bit Java 1.6.0_13.

Update: Clearly I'm just retarded.  It works this way on Windows, on Railo, and everyone else confirms this is known behaviour.  Somehow I've gone 10 years without running into this issue, probably because I always revert to CFSCRIPT when I do "weird" looping.

That syntax above is analogous to collection iteration (in Groovy):

(1..10).each { i ->
  println(i)
  if (i == 3) {
    i += 3
  }
}

It is NOT analogous to this for loop (also in Groovy):

for (i = 1; i <= 10; i += 1) {
  println(i)
  if (i == 3) {
    i += 3
  }
}

Weird.

13 responses to “Riddle Me This, CFLOOP”

  1. rob

    I get the same thing as you. cf8 Ubuntu 32 bit. It looks like the cfif puts it into another scope. Try this:

    <cfloop from="1" to="10" index="i">
      #i#<br />
      <cfif i EQ 3>
        <cfset i += 3 />
        blarg #i#
      </cfif>
    </cfloop>
    

    You'll probably see, like I do:

    1
    2
    3
    blarg 6
    4
    5
    6
    7
    8
    9
    10

    However, I expected the same thing you expected.

  2. rob

    Ah, comments in tags. I basically just put "blarg #i#" inside of the cfif block.

  3. rob

    rather, tags in comments :-/

  4. Sean Corfield

    Not sure why you think this is unusual? You've specified that the loop runs from 1..10. Changing the loop variable inside the loop is not going to affect the actual loop :)

    Hint: it works like this in some other languages too (but not all/most).

    So it depends on which languages you have exposure to…

  5. Andrew Scott

    @Sean, I disagree with you. Although this is what it is doing, one should be able to manually change the step value.

    I wold file that as a bug, because that is exactly what it is.

  6. Randy Merrill

    From the livedocs:

    http://livedocs.adobe.com/coldfusion/8/htmldocs/Tags_j-l_11.html#1101087

    Note: The to value is evaluated once, when the cfloop tag is encountered. Any change to this value within the loop block, or within the expression that evaluates to this value, does not affect the number of times the loop is executed.

    It is definately a little strange…

  7. Jamie Krug

    Interesting. In cfscript, I guess since the conditions of the loop are more explicit, an equivalent loop produces different output:

    for (i=1; i<=10; i++) {
    writeOutput(i & ");
    if (i==3) i+=3;
    }

    @Sean: makes sense, but still a little odd to me. Even more strange that cfloop and cfscript-for behave so differently — also makes sense, just odd. Another cfscript vs tags quirk, I guess.

    Here's a tag-based alternative:

    <cfset i = 1 />
    <cfloop condition="i LTE 10″>
    #i#<br />
    <cfif i EQ 3>
    <cfset i += 3 />
    </cfif>
    <cfset i++ />
    </cfloop>

    BTW, this also reminds me of a quirk I discovered a while back with grouped query output and the currentRow value. If you inspect the currentRow value within the inner cfoutput in a grouped query output, you'll find that it does not increment with inner loop iterations, but only the outer. I found this to be expected behavior. I can see both sides of the argument there.

  8. Jamie Krug

    To be clear, both examples in my prior comment produce the following output:
    1
    2
    3
    7
    8
    9
    10

  9. anthony

    It seems as though cfloop resets the variable with every iteration of the loop, so once the loop starts over again, i gets reset. What's strange to me is that i is local to each iteration, but once it breaks from the loop, i is no longer a local variable. The following will out put "11″ after the loop (which also doesn't make sense).

    #i#

    #i#

  10. anthony

    oops. lets try the code again.
    <cfloop from="1″ to="10″ index="i">
    #i#<br />
    </cfloop>
    #i#

  11. Justin Carter

    I don't think the syntax could be much clearer – loop over the numbers from 1 to 10, and you can access the current index with "i". If you need absolute control over the index then you should set it before the loop, use a condition for the looping, and manage the index yourself during the loop. Not all looping constructs are created equal, but they all have their uses :)