Loops II - Safety and Efficiency

Discussion in 'Teaching Programming with B4X' started by wonder, Oct 15, 2015.

  1. wonder

    wonder Expert Licensed User

    As you learned before, using loops will avoid code repetition and it can be a very efficient way to get things done.
    However, if not handled properly, they do also hold the danger of freezing your entire app and/or causing an ANR warning.


    Caution level:

    1. The least "dangerous" kind of loop is the For...Next loop, because in principle it will "always end".
    Consider the following code:
    Code:
    'This code will most probably cause an ANR.
    For i = 0 to 999999
        
    If File.Exists(File.DirAssets, i & ".txt"Then Log("True")
    Next
    Depending on the hardware, this example will indeed take some (maybe a lot of) time to complete, but nevertheless we can rest assured it will (eventually) complete.

    2. The Do While/Until...Loop cycle should be regarded as a more "dangerous" kind of loop and has be used more responsibly.
    Code:
    'This code will most probably cause an ANR.
    Do Until n = 14
        
    Dim n = Rnd(09999999999As Long
    Loop
    This loop should end in a few seconds (minutes? hours?), but who knows, it might run forever. This kind of approach is not advisable at all.

    Safety:

    If you really have to use a "dangerous" loop in your app, a good practice would be implementing a timeout. Basically, exit the loop if it starts taking too long.
    Code:
    Dim loopTimeout = 10000 '(ms) = 10 seconds
    Dim loopStart = DateTime.Now As Long
    Do (Until n = 14Or (DateTime.Now - loopStart >= loopTimeout)
       
    Dim n = Rnd(0999999as Int
    Loop
    This way you will always ensure that there will be an exit point in your loop. Optionally you may handle the timeout after you exit the loop. Here's how:
    Code:
    Dim timeoutError = False As Boolean
    Dim loopTimeout = 10000 '(ms) = 10 seconds
    Dim loopStart = DateTime.Now As Long
    Do Until n = 14 _
    Or timeoutError
       
    Dim n = Rnd(0999999as Int
       
    If (DateTime.Now - loopStart >= loopTimeout) Then timeoutError = True
    Loop
    If timeoutError Then
        
    Log("Sorry, bla bla bla...")
        ...
    End If

    Efficiency:

    Not only you can apply the safety device described above, you can as well add some efficiency oriented instructions to your code. For this matter, we will use the Continue and Exit instructions.
    In the example below, the Exit statement will terminate the For loop once the correct file is found.
    Code:
    'Find and process ONLY the most recent yearly report.
    For i = 2015 to 1977 Step -1
        
    If File.Exists(File.DirAssets, "year_report_" & i & ".txt" Then
            ...
            ...
            ...
            ...
            
    Exit
        
    End if
    Next
    ...and here's a way to exit nested loops:
    Code:
    For i = 0 to 100
        
    For j = 0 to 100
            
    If a(i, j) = "GOLD" Then Exit
            ...
            ...
            ...
        
    Next
        
    If a(i, j) = "GOLD" Then Exit
    Next
    Log("Gold was found in [A] at" & i & ", " & j)
    The Continue statement will ignore all the loop code bellow and proceed to the next iteration:
    Code:
    'Do not process ADMIN users
    For i = 0 to (users.Lenght  - 1)
        
    If users(i) = "ADMIN" Then Continue 'at this point the current iteration is terminated and the loop proceeds to the next one.
        ...
        ...
        ...
        ...
        ...
        ...
        ...
    Next
    Of course, the same could be achieved with the traditional If/Then/Else statements but it's good to know we can also do it this way.

    Long-story short: The Exit statement exits the whole loop. The Continue statement exits the current iteration.​
     
    Last edited: Oct 17, 2015
  2. ilan

    ilan Expert Licensed User

    DoEvents is also a very important statement to mention

    If you use a loop in a loop it is better to add DoEvents between the next loop.

    It will take longer to finish this process but it will avoid crashes!


    Code:
    for i = 0 to 100
       
    for i2 = 0 to 100
       
    'enter here you code

      
    next
      
    DoEvents 'to avoid crash
    next
    if you don't want to slow down your loop to much you can add DoEvents only if i mod 5 for example

    Code:
    for i = 0 to 100
      
    for i2 = 0 to 100
      
    'enter your code here

      
    next
      
    if i mod 5 = 0 then DoEvents
    next
     
    wonder likes this.
  3. HotShoe

    HotShoe Well-Known Member Licensed User

    Infinite loops are indeed a pain in the While and Until types of loops. I can't tell you how many times I have "created" one or more in some late night coding session. This is where the "think like a machine" starts to really pay off. Great addition @wonder.
     
  4. Informatix

    Informatix Expert Licensed User

    I'd just like to mention that a more efficient and more generic version would be:
    Code:
    Dim ExitLoops As Boolean
    For i = 0 to 100
        
    For j = 0 to 100
            
    If a(i, j) = "GOLD" Then
                  ExitLoops = 
    True
                  
    Exit
            
    End If
            ...
            ...
            ...
        
    Next
        
    If ExitLoops Then Exit
    Next
    Log("Gold was found in [A] at" & i & ", " & j)
    And a word about performance:
    A loop like this:
    Code:
    For Each Item As String In MyList
        ...
    Next
    is faster than:
    Code:
    Dim i As Int
    Do While i < MyList.Size
       
    Dim Item As String = MyList.Get(i)
       ...
       i = i + 
    1
    Loop
    which is faster than:
    Code:
    For i = 0 to MyList.Size - 1
        
    Dim Item As String = MyList.Get(i)
        ...
    Next
     
  5. ilan

    ilan Expert Licensed User

    Code:
    dim maxnr as int = 100
    For i = 0 to maxnr
        
    For j = 0 to maxnr
            
    If a(i, j) = "GOLD" Then i = maxnr : j = maxnr
            ...
            ...
            ...
        
    Next
    Next
    Log("Gold was found in [A] at" & i & ", " & j)
    also a possibility and also less lines :D
     
  6. Erel

    Erel Administrator Staff Member Licensed User

    This syntax is "incorrect":
    Code:
    If a(i, j) = "GOLD" Then i = maxnr : j = maxnr
    The second part of this line will always be executed.

    You need to write:
    Code:
    If a(i, j) = "GOLD" Then
      i = maxnr
      j = maxnr
    End If
     
  7. Informatix

    Informatix Expert Licensed User

    And I did not like to maintain such a code...
     
  8. Informatix

    Informatix Expert Licensed User

    Exit is missing in the condition; without it, the code after the condition will be run.
     
  9. ilan

    ilan Expert Licensed User

    yes you are right, in VB it would work.

    this is not correct because i set the value i and j to the maximum number of the loop so it will exit, you can see it in the screenshot i attached (log)

    i agree with you, i have never used such a way to exit my loops, i used normally the way you suggested by checking a boolean in my loop.
    i just wanted to take part of this discussion ;)

    screen1.jpg
     
  10. wonder

    wonder Expert Licensed User

    Thank you!! I didn't know about this! Mental note taken! I'll add this one to the "<> is faster than NOT()". :)
     
  11. Informatix

    Informatix Expert Licensed User

    Your code works only if the condition is at the end of the inner loop and there's nothing else in the other loop but it's not what's implied by the wonder's code (note the lines with ...) and there's a faster way to find a value in an array otherwise.
     
  12. wonder

    wonder Expert Licensed User

    By the way, I've created all the examples in notepad. Don't take them too seriously. :D
     
    Peter Simpson and LucaMs like this.
  13. ilan

    ilan Expert Licensed User

    The end of the inner loop is 100 but you can see that i finish the loop with 78 (see logs)

    So it will exit in any condition because i change the value to the maximum of the loop and like this there will not be any more loops.

    Again its just a way not really the way i would choose or recommendet to someone else. Your suggestion is the correct one but my would also work :)

    Try it yourself.
     
  14. Informatix

    Informatix Expert Licensed User

    You did not read my explanation.
    Here's a case where your code should not be used:
    Code:
    For i = 0 To WhateverYouWant
            
    For j = 0 To WhateverYouWant
                
    If Myarray(i, j) = "Gold" Then
                     i = WhateverYouWant
                     j = WhateverYouWant
                
    End If
                
    If i > 50 Then
                   Myarray(i, j) = 
    "Silver"
                
    Else
                   Myarray(i, j) = 
    "Platinum"
                
    End If
                
    Log(i & " " & j & " = " & Myarray(i, j))
           
    Next
     
    Next
    MyArray(100, 100) is set to Silver while it should not.
     
    wonder likes this.
  15. ilan

    ilan Expert Licensed User

    i don't want to argue with you or make you mad :D
    but this is not fair

    is like to tell you your code will not work like this:

    Code:
    Dim ExitLoops As Boolean
    For i = 0 to 100
        
    For j = 0 to 100
             ExitLoops = 
    True
            
    If a(i, j) = "GOLD" Then
                  
    Exit
            
    End If
            ...
            ...
            ...
        
    Next
        
    If ExitLoops Then Exit
    Next
    Log("Gold was found in [A] at" & i & ", " & j)
    you should put the condition in the right place and it will work.

    Code:
    For i = 0 To WhateverYouWant
            
    For j = 0 To WhateverYouWant
                
    If i > 50 Then
                   Myarray(i, j) = 
    "Silver"
                
    Else
                   Myarray(i, j) = 
    "Platinum"
                
    End If
                
    Log(i & " " & j & " = " & Myarray(i, j))
                
    If Myarray(i, j) = "Gold" Then
                     i = WhateverYouWant
                     j = WhateverYouWant
                
    End If
           
    Next
    Next
    but i agree with you it is not the right way but it will do the work...
     
    Last edited: Oct 28, 2015
  16. Informatix

    Informatix Expert Licensed User

    You claim that your code is equivalent to the wonder's code. It is not. That's all. I never said it wouldn't be useful in some cases or that it does not work at all.

    NO! It's worse. You should really try the code.
     
  17. Informatix

    Informatix Expert Licensed User

    Just look at your original post. Your code in this post, which is explicitely written as an equivalent to the wonder's code (you even kept the "..."), cannot work as you wrote it. You introduced two errors. Erel explained the first; I explained the second. I don't know why we are losing time on that matter.
     
  18. Daestrum

    Daestrum Well-Known Member Licensed User

    One other thing you need to consider.
    Irrespective of how you write the code, the compiler will apply many optimizations to it, meaning the resultant executable code can be very different to how you write it.

    Stick a '-server' before your jar (if its a server app obviously) and you get the compiler doing it's thing again on load. It will remove dead loops completely.
     
  19. Informatix

    Informatix Expert Licensed User

    Yes, your code can be different after the Dalvik optimization, but no, the final result is still predictable.
    If you write your loops like this:
    Code:
    For i = 0 to MyList.Size - 1
       
    Dim Item AsString = MyList.Get(i)
       ...
    Next
    they won't be transformed like this:
    Code:
    For Each Item As String In MyList
        ...
    Next
    The code which is slower in theory will be still slower in practice (not only for loops). So the way you write your code is very important.
     
  20. Daestrum

    Daestrum Well-Known Member Licensed User

    Agreed but looking at the actual code produced (possibly down to how b4x translates into java) there is very minor differences.
    forloop uses the standard for a = 0 to length-1
    foreach uses for each c in ...
    Yet the resultant code is so similar
    Code:
    publicstaticString _foreach()
    throwsException
    {
    String str = "";
    anywheresoftware.b4a.objects.collections.List localList = _a;
    inti = localList.getSize();
    for(intj = 0; j < i; j++)
    {
    str = BA.ObjectToString(localList.Get(j));
    Common.Log(str);
    }
    return"";
    }


    publicstaticString _forloop()
    throwsException
    {
    inti = 0;
    intj = _a.getSize() - 1;
    for(i = 0; i <= j; i = 0 + i + 1) {
    Common.Log(BA.ObjectToString(_a.Get(i)));
    }
    return"";
    }
     
    Informatix likes this.
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice