Using Labeled Loops In ColdFusion

Ben Nadel
Published: September 2, 2023

The other day, I was watching a tutorial on the Svelte JavaScript framework and I saw that they used the $: notation to denote reactive values. I believe that this is just an old JavaScript feature for labeling parts of the code (which Svelte is then overloading). I’ve never used that JavaScript feature myself; but, it did remind me that some of this labeling concept is also available in ColdFusion as well, specifically for loops. I’ve never used this feature in ColdFusion either; so, I thought it might be worth a quick exploration.

I couldn’t find much on how this feature actually works outside of this article on the ColdFusion blog. The idea is that you can add a label to either a for or a while loop; and then, consume that label in either a break or a continue statement. Essentially, you can tell ColdFusion which loop you are referencing in your break / continue statements.

Which, is really only meaningful if you have nested loops. Otherwise, the break and continue statements simply reference the one contextual loop that you are in.

Part of me feels that labeling loops falls under the “too clever” umbrella of programming approaches; and, is likely to make the code harder to read and maintain. But, if encapsulated behind some meaningful abstraction, it could be OK to use from time to time.

And, to be honest, I had a hard time thinking of a real-world scenario in which I might want to have nested loops with label-based control flow. What I eventually came up with was a fuzzy-matching algorithm for text search. Given a target value and a search value, we want to determine if ever letter within the search value can be located – in order – within the target value, even if not within a contiguous string of characters.

For this, I’m going to have two nested loops:

  • Looping over the search characters.
  • Looping over the target characters.

As I compare one character to another, I’m going to have opportunities to both break and continue from the inner loop to the outer loop. This is hard to explain in words, so let’s take a look at the ColdFusion code (which works in both Adobe ColdFusion and Lucee CFML) – note that the outer loop is labeled searchLoop: and the inner loop is labeled targetLoop::

<cfscript>
	// Fuzzy matches.
	writeOutput( isFuzzyMatch( "horse", "s" ) & "<br />" );
	writeOutput( isFuzzyMatch( "horse", "hs" ) & "<br />" );
	writeOutput( isFuzzyMatch( "horse", "horse" ) & "<br />" );
	// No matches.
	writeOutput( isFuzzyMatch( "horse", "horses" ) & "<br />" );
	writeOutput( isFuzzyMatch( "horse", "test" ) & "<br />" );
	writeOutput( isFuzzyMatch( "horse", "" ) & "<br />" );
	// ------------------------------------------------------------------------------- //
	// ------------------------------------------------------------------------------- //
	/**
	* I determine if the given target text is a fuzzy-match for the given search text.
	*/
	public boolean function isFuzzyMatch(
		required string targetValue,
		required string searchValue
		) {
		var searchChars = searchValue.listToArray( "" );
		var targetChars = targetValue.listToArray( "" );
		var matchFound = false;
		searchLoop:
		while ( searchChars.len() ) {
			var s = searchChars.shift();
			targetLoop:
			while ( targetChars.len() ) {
				var t = targetChars.shift();
				// We found a matching CHARACTER in the target string.
				if ( s == t ) {
					if ( ! searchChars.len() ) {
						matchFound = true;
						// If we've run out of search-characters to consume, it means that
						// the entirety of the search keyword was located (in parts)
						// within the target text. In other words, we HAVE a fuzzy-match.
						// Yay! At this pointer, there is nothing left to search and we
						// can break out of BOTH the INNER and OUTER loops.
						break searchLoop;
					}
					// If we still have more search characters to consume, move onto the
					// NEXT ITERATION of the OUTER loop, and the next search character.
					continue searchLoop;
				}
			}
			// If we've fully consumed the target characters, there's no sense in
			// continuing to consume the search characters - we will not find a match.
			break;
		}
		return( matchFound );
	}
</cfscript>

NOTE: In this code, there is no need for me to label the inner loop, targetLoop: – I’m just doing it to demonstrate that it can be done.

As you can see, whenever I match a search character against a target character, I perform a control-flow operation that references the outer loop, searchLoop:. If I’ve consumed all of the search characters, I break out of outer loop (essentially ending the algorithm); and, if there are more search characters to consume, I continue onto to the next iteration of the outer loop. Both of these operations are performed from within the inner loop.

If we run this ColdFusion code, we get the expected outcome:

true    <!-- isFuzzyMatch( "horse", "s" ) -->
true    <!-- isFuzzyMatch( "horse", "hs" ) -->
true    <!-- isFuzzyMatch( "horse", "horse" ) -->
false   <!-- isFuzzyMatch( "horse", "horses" ) -->
false   <!-- isFuzzyMatch( "horse", "test" ) -->
false   <!-- isFuzzyMatch( "horse", "" ) -->

This demo uses script-based loops; but, apparently this is also available in tag-based loops as well.

I’m not sure if the value-add of this technique outweighs the potential cognitive cost. But, it’s probably worth having this loop labeling concept tucked away in the back of my mind in case I ever need it.

Want to use code from this post?
Check out the license.

Source: www.bennadel.com