Jump to content

When a Loop isn't a Loop


czardas
 Share

Go to solution Solved by Bowmore,

Recommended Posts

In the following scenario I wish to run a sequence of tests until a satisfactory condition is met. Then the remaining tests are skipped.

;

Local $bCondition = False

Do
    ; test 1
    If $bCondition Then ExitLoop

    ; test 2
    If $bCondition Then ExitLoop

    ; Debug or get out of the script.

Until True ; Loop will not repeat.

; continue...

;

I could do the same thing in other ways but this is nice and clear: keeping everything in one place has certain merits. The problem I have is trying to convince myself that using a loop which never repeats (Do...Until True) actually qualifies as good programming. I keep asking myself why use a loop at all when it isn't really needed?

Do you think I should do this differently, or should I leave it the way it is? :think:

Edited by czardas
Link to comment
Share on other sites

The condition "true" will be met in just one pass I suppose. (and no need to repeat the test to check again if some condition has changed)
so if the true condittion will not be met, you will be catch in an endless loop or you need an one further check at the end

maybe you could use a (For ...  next) loop instead of (Do...Until True) to avoid the last check.... ?
 

For $i = 1 To 1 ; only one pass anyway

    ; test 1
    If $bCondition Then ExitLoop

    ; test 2
    If $bCondition Then ExitLoop

Next

; continue...

edit:

corrected 0 to 1 with 1 to 1

Edited by Chimp

 

image.jpeg.9f1a974c98e9f77d824b358729b089b0.jpeg Chimp

small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt....

Link to comment
Share on other sites

If the loop is never repeated it is useless (IMHO).

Why not simply remove it and add some comment lines to reach your goal: "nice and clear: keeping everything in one place"

; --------------
; Test code starts here
; --------------
Local $bCondition = False
; test 1
If Not $bCondition Then Test1()
; test 2
If Not $bCondition Then Test2()
; --------------
; End of Tests 
; --------------

; continue.

My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2024-07-28 - Version 1.6.3.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
OutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - Download
Outlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - Wiki
PowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - Wiki
Task Scheduler (2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki

Standard UDFs:
Excel - Example Scripts - Wiki
Word - Wiki

Tutorials:
ADO - Wiki
WebDriver - Wiki

 

Link to comment
Share on other sites

 

If the loop is never repeated it is useless (IMHO).

Why not simply remove it and add some comment lines to reach your goal: "nice and clear: keeping everything in one place"

; --------------
; Test code starts here
; --------------
Local $bCondition = False
; test 1
If Not $bCondition Then Test1()
; test 2
If Not $bCondition Then Test2()
; --------------
; End of Tests 
; --------------

; continue.

 

Having to keep testing conditions will impact performance. The idea is to skip all subsequent tests and conditions.

Edited by czardas
Link to comment
Share on other sites

Chimp - Your loop For ($i = 0 To 1) will run twice. I modelled the approach on the expression While 1: being equivalent to While True. It appears to work in the same way.

 

.... I was editing my post while you was typing.....

anyway, (for next) should avoid possible endless loops....

Edited by Chimp

 

image.jpeg.9f1a974c98e9f77d824b358729b089b0.jpeg Chimp

small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt....

Link to comment
Share on other sites

How many tests do you run? In your OP you are testing the condition as well. Maybe not as often as in my code but such a test doesn't increase runtime dramatically.

My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2024-07-28 - Version 1.6.3.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
OutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - Download
Outlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - Wiki
PowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - Wiki
Task Scheduler (2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki

Standard UDFs:
Excel - Example Scripts - Wiki
Word - Wiki

Tutorials:
ADO - Wiki
WebDriver - Wiki

 

Link to comment
Share on other sites

Hmm, conditions are sometimes inside other loops. Here's a snapshot.

;

Do
    ; First attempt to divide by factors of 10
    While StringRight($aSciNotation[$iLo][0], 1) = "0"
        $aSciNotation[$iLo][0] = Execute(StringTrimRight($aSciNotation[$iLo][0], 1))
        $aSciNotation[$iHi][1] -= 1
        If $aSciNotation[$iHi][1] = 0 Then ExitLoop 2 ; no need to go any further
    WEnd

    ; More loops and conditions.

Until True
Link to comment
Share on other sites

Then I would stick with your current approach.

Seems to be the best way to do what you want.

My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2024-07-28 - Version 1.6.3.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
OutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - Download
Outlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - Wiki
PowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - Wiki
Task Scheduler (2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki

Standard UDFs:
Excel - Example Scripts - Wiki
Word - Wiki

Tutorials:
ADO - Wiki
WebDriver - Wiki

 

Link to comment
Share on other sites

Thanks water.

I suppose the more standard approach would be to remove this part of the code to a different function and return according to whatever condition. I could still do that, but I think it might make it harder to follow the flow of arguments. Keeping everything in one place also avoids having to pass a number of parameters to a different function. I asked because I was wondering what opinions people might have.

Edited by czardas
Link to comment
Share on other sites

When I start a new project my code should be as clean as possible (means: adhere to good coding practices. A good starting point is the "Good Coding Practices" thread on the forum).

Over time the code gets more complex as new functionality gets implemented.  The script should be so well documented that you still understand what you have intented with your code even when you have a look at it again a few years later.

My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2024-07-28 - Version 1.6.3.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
OutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - Download
Outlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - Wiki
PowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - Wiki
Task Scheduler (2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki

Standard UDFs:
Excel - Example Scripts - Wiki
Word - Wiki

Tutorials:
ADO - Wiki
WebDriver - Wiki

 

Link to comment
Share on other sites

Is it not feasible to just use Else and Return?

Make sure brain is in gear before opening mouth!
Remember, what is not said, can be just as important as what is said.

Spoiler

What is the Secret Key? Life is like a Donut

If I put effort into communication, I expect you to read properly & fully, or just not comment.
Ignoring those who try to divert conversation with irrelevancies.
If I'm intent on insulting you or being rude, I will be obvious, not ambiguous about it.
I'm only big and bad, to those who have an over-active imagination.

I may have the Artistic Liesense ;) to disagree with you. TheSaint's Toolbox (be advised many downloads are not working due to ISP screwup with my storage)

userbar.png

Link to comment
Share on other sites

You might consider recursion.

_Test ()

Func _Test()
    ConsoleWrite("Test condition" & @LF)
    ;if test is not zero
    If Random(0, 10, 1) Then
        Sleep(10)
        _Test()
    EndIf
    _Continue()
EndFunc

Func _Continue()
    ConsoleWrite("Condition met" & @LF)
    Exit
EndFunc

EDIT:

I don't know if this is pertinent or just stupid, but you could also unwind the recursion, but that ironically requires a loop  :rolleyes:

_Test ()

Func _Test()
    ConsoleWrite("Test condition" & @LF)
    Local Static $Unwind = 0
    ;if test is not zero
    If Random(0, 10, 1) Then
        $Unwind += 1
        Sleep(10)
        _Test()
    EndIf
    While $Unwind
        ConsoleWrite("Unwind" & @LF)
        $Unwind -= 1
        Return
    WEnd
    _Continue()
EndFunc

Func _Continue()
    ConsoleWrite("Condition met" & @LF)
    Exit
EndFunc
Edited by JohnOne

AutoIt Absolute Beginners    Require a serial    Pause Script    Video Tutorials by Morthawt   ipify 

Monkey's are, like, natures humans.

Link to comment
Share on other sites

Is it not feasible to just use Else and Return?

 

Using Return would exit the function, and not the contained region.

You might consider recursion.

 

Nope! I don't see how that is useful here. It's unfinished code, but here's a bit more of it. I am using ExitLoop() to skip forward however many steps (notice the aborted attempt).

;

Do
        ; Some preprocessing goes here

        If StringLen($aSciNotation[$iHi][0]) + $aSciNotation[$iHi][1] <= 15 Then ExitLoop ; no need to go any further

        ; First attempt to divide by factors of 10
        While StringRight($aSciNotation[$iLo][0], 1) = "0"
            $aSciNotation[$iLo][0] = Execute(StringTrimRight($aSciNotation[$iLo][0], 1))
            $aSciNotation[$iHi][1] -= 1
            If $aSciNotation[$iHi][1] = 0 Then ExitLoop 2 ; no need to go any further
        WEnd

        ; Attempt division by factors of 5, followed by factors of 2.
        Local $iExpansion, $aFactor[2] = [5, 2]
        For $i = 0 To 1
            While Mod($aSciNotation[$iLo][0], $aFactor[$i]) = 0 ; Integer division is possible.
                $iExpansion = Int($aSciNotation[$iHi][0] * $aFactor[1 - $i])
                If $iExpansion >= 1.0e+15 Then ExitLoop ; Abort the current attempt.

                $aSciNotation[$iHi][0] = $iExpansion
                $aSciNotation[$iLo][0] = Int($aSciNotation[$iLo][0] / $aFactor[$i])
                $aSciNotation[$iHi][1] -= 1
                If $aSciNotation[$iHi][1] = 0 Then ExitLoop 3 ; no need to go any further
            WEnd
        Next

        ; More attempts to achieve the desired result.

    Until True

    ; Code continues inside the function.
Edited by czardas
Link to comment
Share on other sites

I guess this is a little bit more readable than nested if statements, which most would do I guess (including me). I have seen your approach in the Crypt UDF by the way. Is it better? Actually I don't know if anything is wrong with it. I am stumped.

UDF List:

 
_AdapterConnections()_AlwaysRun()_AppMon()_AppMonEx()_ArrayFilter/_ArrayReduce_BinaryBin()_CheckMsgBox()_CmdLineRaw()_ContextMenu()_ConvertLHWebColor()/_ConvertSHWebColor()_DesktopDimensions()_DisplayPassword()_DotNet_Load()/_DotNet_Unload()_Fibonacci()_FileCompare()_FileCompareContents()_FileNameByHandle()_FilePrefix/SRE()_FindInFile()_GetBackgroundColor()/_SetBackgroundColor()_GetConrolID()_GetCtrlClass()_GetDirectoryFormat()_GetDriveMediaType()_GetFilename()/_GetFilenameExt()_GetHardwareID()_GetIP()_GetIP_Country()_GetOSLanguage()_GetSavedSource()_GetStringSize()_GetSystemPaths()_GetURLImage()_GIFImage()_GoogleWeather()_GUICtrlCreateGroup()_GUICtrlListBox_CreateArray()_GUICtrlListView_CreateArray()_GUICtrlListView_SaveCSV()_GUICtrlListView_SaveHTML()_GUICtrlListView_SaveTxt()_GUICtrlListView_SaveXML()_GUICtrlMenu_Recent()_GUICtrlMenu_SetItemImage()_GUICtrlTreeView_CreateArray()_GUIDisable()_GUIImageList_SetIconFromHandle()_GUIRegisterMsg()_GUISetIcon()_Icon_Clear()/_Icon_Set()_IdleTime()_InetGet()_InetGetGUI()_InetGetProgress()_IPDetails()_IsFileOlder()_IsGUID()_IsHex()_IsPalindrome()_IsRegKey()_IsStringRegExp()_IsSystemDrive()_IsUPX()_IsValidType()_IsWebColor()_Language()_Log()_MicrosoftInternetConnectivity()_MSDNDataType()_PathFull/GetRelative/Split()_PathSplitEx()_PrintFromArray()_ProgressSetMarquee()_ReDim()_RockPaperScissors()/_RockPaperScissorsLizardSpock()_ScrollingCredits_SelfDelete()_SelfRename()_SelfUpdate()_SendTo()_ShellAll()_ShellFile()_ShellFolder()_SingletonHWID()_SingletonPID()_Startup()_StringCompact()_StringIsValid()_StringRegExpMetaCharacters()_StringReplaceWholeWord()_StringStripChars()_Temperature()_TrialPeriod()_UKToUSDate()/_USToUKDate()_WinAPI_Create_CTL_CODE()_WinAPI_CreateGUID()_WMIDateStringToDate()/_DateToWMIDateString()Au3 script parsingAutoIt SearchAutoIt3 PortableAutoIt3WrapperToPragmaAutoItWinGetTitle()/AutoItWinSetTitle()CodingDirToHTML5FileInstallrFileReadLastChars()GeoIP databaseGUI - Only Close ButtonGUI ExamplesGUICtrlDeleteImage()GUICtrlGetBkColor()GUICtrlGetStyle()GUIEventsGUIGetBkColor()Int_Parse() & Int_TryParse()IsISBN()LockFile()Mapping CtrlIDsOOP in AutoItParseHeadersToSciTE()PasswordValidPasteBinPosts Per DayPreExpandProtect GlobalsQueue()Resource UpdateResourcesExSciTE JumpSettings INISHELLHOOKShunting-YardSignature CreatorStack()Stopwatch()StringAddLF()/StringStripLF()StringEOLToCRLF()VSCROLLWM_COPYDATAMore Examples...

Updated: 22/04/2018

Link to comment
Share on other sites

Link to comment
Share on other sites

  • Solution

I write lots of data validation routines that run several hundred validation checks on each row of data and I use the same sort of structure as sown in your first post. The alternative would be a ridiculous set of nested IFs or ElseIfs. The loop that never loops in combination with If False Then ExitLoop  statements is my opinion an elegant way to implement this for the following reasons:

  • It is clear to read and understand.
  • It is easy to add additional tests
  • The order of the tests is easy to change
  • It can be optimised by putting the tests most likely to fail at the top.
Edited by Bowmore

"Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the universe trying to build bigger and better idiots. So far, the universe is winning."- Rick Cook

Link to comment
Share on other sites

Bowmore - Indeed there are some benefits to using a construct like this. My code posted above will require some optimization later. Tests need to be run for fine tuning etc... Working this way is easy. :)

Chimp - There's hardly any difference using a For loop. In fact the way I am doing this does not require the comparison of an incremented loop iteration count.

Thanks for all your responces. I would like to mark this solved, but there are a number of best answers here. I'll just pick one. :D

Edited by czardas
Link to comment
Share on other sites

I finally read the topic correctly,.I suppose I can see why this approach might suit some folk.

Personally I think Functions are as modular as I need. Depending how much code was in the tests, I might find it more difficult to follow.

Edited by JohnOne

AutoIt Absolute Beginners    Require a serial    Pause Script    Video Tutorials by Morthawt   ipify 

Monkey's are, like, natures humans.

Link to comment
Share on other sites

If the code did not sit so well, I would normally call another function. There are several other variables involved in the following part of the loop including an extended return value for the current function. Since the data is manipulated in various ways in an attempt to achive a single objective - the code belongs together in one place. Passing lots of ByRef parameters is the alternative.

Edited by czardas
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...