For/Next Loop Limit Evaluations

Hey all,

I’ve run across an issue with a For/Next loop regarding evaluations of the loop parameters that I’d like to understand better.

Here’s the code in question:

      Dim ThePortStrings() as String = PortString.Split(RangeDelimeter)
      Dim NewPortString as string = ThePortStrings.Pop
      
      For i as integer = ThePortStrings.Ubound DownTo 0
        
        NewPortString = ThePortStrings.Pop+RangeDelimeter+NewPortString
        
        If NewPortString.Len + ThePortStrings(ThePortStrings.Ubound).Len > 52 Then
          InterfaceStringArray.Append NewPortString
          NewPortString = ThePortStrings.Pop
        End If
        
      Next

So I would figure that each time the For/Next loop is run that the value of I would be checked based on the expression in the loop. So for each iteration, ThePortStrings.Ubound would be checked. That doesn’t seem to be the case.

I’m seeing an issue where if I go into the IF/THEN branch and Pop off another element of PortStrings, the next loop runs even if ThePortStrings.ubound is now -1. My thought was that gets evaluated each pass in the loop.

What I am trying to do is take a string that could be over 52 characters long and break it down into multiple strings of no more than 52 characters.

I know I can do this instead with a Do Until Loop or a While/Wend and that’s probably better as I am never utilizing the value of “i” in the For/Next Loop. Just trying to satisfy my curiosity about when the expression at the beginning is evaluated…

[quote=414262:@Jon Ogden]Hey all,

I’ve run across an issue with a For/Next loop regarding evaluations of the loop parameters that I’d like to understand better.

Here’s the code in question:

      Dim ThePortStrings() as String = PortString.Split(RangeDelimeter)
      Dim NewPortString as string = ThePortStrings.Pop
      
      For i as integer = ThePortStrings.Ubound DownTo 0
        
        NewPortString = ThePortStrings.Pop+RangeDelimeter+NewPortString
        
        If NewPortString.Len + ThePortStrings(ThePortStrings.Ubound).Len > 52 Then
          InterfaceStringArray.Append NewPortString
          NewPortString = ThePortStrings.Pop
        End If
        
      Next

So I would figure that each time the For/Next loop is run that the value of I would be checked based on the expression in the loop. So for each iteration, ThePortStrings.Ubound would be checked. That doesn’t seem to be the case.

I’m seeing an issue where if I go into the IF/THEN branch and Pop off another element of PortStrings, the next loop runs even if ThePortStrings.ubound is now -1. My thought was that gets evaluated each pass in the loop.

What I am trying to do is take a string that could be over 52 characters long and break it down into multiple strings of no more than 52 characters.

I know I can do this instead with a Do Until Loop or a While/Wend and that’s probably better as I am never utilizing the value of “i” in the For/Next Loop. Just trying to satisfy my curiosity about when the expression at the beginning is evaluated…[/quote]

With this loop counting down ubound is evaluated once when the loop starts.

OK. Thank you. I thought Ubound would be evaluated each time through the loop which is why I’ve seen some people recommend to put Ubound into a variable prior to starting the loop so that it doesn’t get re-evaluated each time through. In this case, I WANTED it to be re-evaluated each time.

Thank you for correcting my misunderstanding…

Interesting, looking at https://documentation.xojo.com/api/code_execution/for...next.html its only End that gets checked every loop, so this should work as expected as you’re not using i it doesn’t really matter on the order:

For i as integer = 0 to ThePortStrings.Ubound

OK. From the Language Reference:

So the endValue is re-evaluated each iteration. The start value is not. So what I was told was correct:

For I as integer = 0 to SomeArray.Ubound

Results in Ubound being called each iteration.

But

For I as integer = SomeArray.Ubound DownTo 0

Results in Ubound only being evaluated at the start of iterations…

Got it…

[quote=414271:@JulianS]Interesting, looking at https://documentation.xojo.com/api/code_execution/for...next.html its only End that gets checked every loop, so this should work as expected as you’re not using i it doesn’t really matter on the order:

For i as integer = 0 to ThePortStrings.Ubound

Yeah, I guess that’s correct. I could do that… Good call.

Adapted from what I used ages ago to flatten HTMLEdit for Xojo back in the day.

const kLength = 52

dim sInput as String = "asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf-asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf-"
dim arsChunks() as String

while sInput.Len > 0
  arsChunks.Append(sInput.Left(kLength))
  
  // +1 because of 0/1 inconsistencies in Xojo
  sInput = sInput.Mid(kLength + 1)
  
wend

Yeah, Julian, that doesn’t work. As I is incrementing and Ubound is decrementing, there’s a point where they will cross and then the loop exists, but I’ve not completely depleted the array. I want to completely deplete ThePortStrings.

So I’ll switch to a Do Loop and check for the Ubound value each pass.

[quote=414278:@Tim Parnell]Adapted from what I used ages ago to flatten HTMLEdit for Xojo back in the day.

[code]
const kLength = 52

dim sInput as String = “asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf-asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf-”
dim arsChunks() as String

while sInput.Len > 0
arsChunks.Append(sInput.Left(kLength))

// +1 because of 0/1 inconsistencies in Xojo
sInput = sInput.Mid(kLength + 1)

wend
[/code][/quote]

Thanks Tim! Except I have to work with arrays like I showed as I’m creating a port interface string for a managed switch and I need a specific format to the resulting command.

I honestly can’t tell what you’re up to from the sample in the original post, so I can’t offer much more help. However, what I shared will take a string and return it separated out into the desired size chunks. It would be possible to use it as a function, and integrate that with however you need. :slight_smile:

Hi Tim,

What I am doing is building a string to address multiple ports on a managed network switch. These command strings look something like the form:

interface range gi1/0/18,gi1/0/22-24,gi2/0/1-12,gi2/0/14,gi2/0/17-20,gi2/0/25-28

My method (I didn’t post all of it), generates everything after the word range in the above example.

Now on some models like the Cisco Catalyst 3750, the switch chokes when the command is too long. I guess we must be talking some 1990s or early 2000s command interface for some of these switches. In this particular case, the last set of ports, gi2/0/25-28, makes the string too long. So I need to break it up and send the first interface range command, do what I need with those ports and then come along and send a second interface range command and do the same work on those switch ports. Given that I am building a string in a particular format to address a set of ports, I can’t just set it to 52 characters. That particular length could be in the middle of one of the comma separated groupings. So I have to use more intelligence.

So I take the port string: gi1/0/18,gi1/0/22-24,gi2/0/1-12,gi2/0/14,gi2/0/17-20,gi2/0/25-28 and split into an array based on the delimiter used by the particular switch, in this case a comma.

Then starting from the last element and working backwards (I could work forwards but it’s easier to just work backwards with the Array.Pop function), I start constructing a string of ports. Then I go into the loop. I now pop in the new last element of the array, add the delimiter (comma) and add it to the front of my existing string.

Next, I check to see if this new string of characters plus the last remaining item in the array will be longer than 52 characters. If it is, I then add the string I have created to a string array property that is stored for later use and then I re-initialize the port string and start over and create a new string to be added to the array property. If not, I continue on and until I hit the length limit or until I run out of elements to “pop.”

So here’s the final code. The Do Until Loop works well.

[code]
If PortString.Len > 52 Then // Check to see if PortString is longer than 52 characters…

  // Split PortString into an array and create a new string from the last element of that array
  Dim ThePortStrings() as String = PortString.Split(RangeDelimeter)
  Dim NewPortString as string = ThePortStrings.Pop
  
 // Now start creating the new sets of strings
  Do Until ThePortStrings.Ubound < 0

    // Pop the current last element, add the delimiter and add it to the existing string
    NewPortString = ThePortStrings.Pop+RangeDelimeter+NewPortString
    
    // Check to see if Popping the next element of the array will make us longer than 52.  If so then
   //  stop there.  Save the existing string to the array property and then reinitialize the new 
  //  string by popping the last array element onto it.
    If NewPortString.Len + ThePortStrings(ThePortStrings.Ubound).Len > 52 Then
      InterfaceStringArray.Append NewPortString
      NewPortString = ThePortStrings.Pop
      
     // check to see if our PortString array has been depleted.  If so, save what we have 
    // created to the array
      If ThePortStrings.Ubound < 0 Then
        InterfaceStringArray.Append NewPortString
      End If
      
    End If
  Loop
End If
[/code]