Better method than GoTo for Guard Clause at the start of a loop? (1 Viewer)

InstructionWhich7142

Registered User.
Local time
Today, 15:40
Joined
Feb 24, 2010
Messages
199
My old functions were serious pyramid code, often multiple nested if's deep, since then I've been using guard clauses and setting values/checking values though a series of separate if's instead of nesting, it's definitely easier to keep track of the "end" compared to nesting!

However I'm stuck when it comes to loops as there's no "continue" function to "exit sub" from that loop based on the guard clause, I suppose I could call the function repeatedly instead of it being a loop, but that wouldn't work for a nested loop that's relying on values from the main function, I know they could be passed but that's back to getting harder to read and jumping around more than "goto SkipLoop" would be?

I'm not bothered either way about goto or not goto, but the convention is there for a reason and I'm curious to have other opinions on best practice
 

CJ_London

Super Moderator
Staff member
Local time
Today, 15:40
Joined
Feb 19, 2013
Messages
16,614
You have the exit option

without seeing your code it might be
Exit do
Exit for

etc
 

InstructionWhich7142

Registered User.
Local time
Today, 15:40
Joined
Feb 24, 2010
Messages
199
yea but that doesn't let me skip to the next in the set
 

The_Doc_Man

Immoderate Moderator
Staff member
Local time
Today, 09:40
Joined
Feb 28, 2001
Messages
27,187
Let me state it another way to be sure of what you mean.

You are looking for a way to end the current iteration of loop without ending the whole loop. Is that it?
 

InstructionWhich7142

Registered User.
Local time
Today, 15:40
Joined
Feb 24, 2010
Messages
199
Let me state it another way to be sure of what you mean.

You are looking for a way to end the current iteration of loop without ending the whole loop. Is that it?
Yes exactly, sorry I was struggling to explain that, I want to skip the rest of the code i've got "in that iteration" and move onto the next record

This is basically what I'm trying to do, i've only written a bit of it and got to the first "guard clause" type bit and was like, "huh" i wonder how best to handle this

currently I've got that GoTo skipping past all the 'enter code here bits and the example "sub" loop that I'll have where I'll be going through a recordset (list of required stock components), probably with another sub look below that (list of stock in warehouse)

Code:
Function Func1()

Dim Var3 As Long
Dim Var2 As Long
Dim Var1 As Long

Do Until DCount("field", "table", "done = false") = 0
    
    Var3 = DLookup("field", "table", "done = false")
    DoCmd.RunSQL "update table set done = true where field = " & Var3 & ""
    
    'valid wo
    If DCount("field2", "table2", "field = " & Var3 & " and outstanding > 0") = 0 Then
        MsgBox "invalid Var"
        GoTo SkipLoop
    End If
    
    
    'do a whole bunch of stuff
    
    Do Until Var1 = Var2
    
    Loop
    
    'do other stuff


SkipLoop:
Loop

MsgBox "Fin", vbSystemModal

End Function
 

Josef P.

Well-known member
Local time
Today, 16:40
Joined
Feb 2, 2023
Messages
826
If you divide the responsibilities among several procedures, aborting is easier.

Code:
Function Func1()

   Dim Var3 As Long

   Do Until DCount("field", "table", "done = false") = 0
      Var3 = DLookup("field", "table", "done = false")
      DoSometingWithField Var3
   Loop

   MsgBox "Fin", vbSystemModal

End Function

Private Sub DoSometingWithField(ByVal Var3 As Long)

   Dim Var2 As Long
   Dim Var1 As Long

   DoCmd.RunSQL "update table set done = true where field = " & Var3 & ""

   'valid wo
   If DCount("field2", "table2", "field = " & Var3 & " and outstanding > 0") = 0 Then
       MsgBox "invalid Var"
       Exit Sub
   End If

   'do a whole bunch of stuff

   Do Until Var1 = Var2

      CallAnotherProc ...

   Loop

   'do other stuff

End Sub

Note: I only restructured the code. I intentionally ignored possible optimizations.
 
Last edited:

pbaldy

Wino Moderator
Staff member
Local time
Today, 07:40
Joined
Aug 30, 2003
Messages
36,125
Another option is using logic:

Code:
Do Until DCount("field", "table", "done = false") = 0
   
    Var3 = DLookup("field", "table", "done = false")
    DoCmd.RunSQL "update table set done = true where field = " & Var3 & ""
   
    'valid wo
    If DCount("field2", "table2", "field = " & Var3 & " and outstanding > 0") = 0 Then
        MsgBox "invalid Var"
    Else
  
      'do a whole bunch of stuff
   
      Do Until Var1 = Var2
   
      Loop
   
      'do other stuff
  End If


Loop
 

InstructionWhich7142

Registered User.
Local time
Today, 15:40
Joined
Feb 24, 2010
Messages
199
If you divide the responsibilities among several procedures, aborting is easier.

Code:
Function Func1()

   Dim Var3 As Long


   Do Until DCount("field", "table", "done = false") = 0

      Var3 = DLookup("field", "table", "done = false")
      DoSometingWithField Var3

   Loop

   MsgBox "Fin", vbSystemModal

End Function

Private Sub DoSometingWithField(ByVal Var3 As Long)

   Dim Var2 As Long
   Dim Var1 As Long

   DoCmd.RunSQL "update table set done = true where field = " & Var3 & ""

   'valid wo

   If DCount("field2", "table2", "field = " & Var3 & " and outstanding > 0") = 0 Then

       MsgBox "invalid Var"
       Exit Sub

   End If

   'do a whole bunch of stuff

   Do Until Var1 = Var2

      CallAnotherProc ...

   Loop

   'do other stuff

End Sub

Hmm, yes I see, it ends up being very similar, just split across two/multiple functions instead of all within a function, which i personally kinda prefer as it's all only doing one "thing", I tend to split useful & reusable things off into separate functions
 

InstructionWhich7142

Registered User.
Local time
Today, 15:40
Joined
Feb 24, 2010
Messages
199
I definitely get the nested if's approach but.... I want to avoid pyramids like this

Code:
Function Func1()

Dim Var3 As Long
Dim Var2 As Long
Dim Var1 As Long

Do Until DCount("field", "table", "done = false") = 0
    
    Var3 = DLookup("field", "table", "done = false")
    DoCmd.RunSQL "update table set done = true where field = " & Var3 & ""
    
    'valid wo
    If DCount("field2", "table2", "field = " & Var3 & " and outstanding > 0") = 0 Then
        MsgBox "invalid Var"
        GoTo SkipLoop
    
    Else
    
        If var4 > 1 Then
                
            Do Until Var1 = Var2
    
                If Var5 > 1 Then
                    
                    DoCmd.RunSQL
                
                Else
                
                    'do something else
                    
                End If
            
            Loop
        
        End If
    
    'do other stuff
        
    End If

SkipLoop:
Loop

MsgBox "Fin", vbSystemModal

End Function
 

Josef P.

Well-known member
Local time
Today, 16:40
Joined
Feb 2, 2023
Messages
826
Hmm, yes I see, it ends up being very similar, just split across two/multiple functions instead of all within a function, which i personally kinda prefer as it's all only doing one "thing", I tend to split useful & reusable things off into separate functions
Only 1 thing? ... i will say a lot of different things ;)
Private procedures do not bother.
Basic idea: single-responsibility principle
 

InstructionWhich7142

Registered User.
Local time
Today, 15:40
Joined
Feb 24, 2010
Messages
199
Only 1 thing? ... i will say a lot of different things ;)
Basic idea: single-responsibility principle
Hehe, 1 thing as in, it's a function for picking stock, chunks of it aren't going to be used for anything else by other code or called from elsewhere etc
 

InstructionWhich7142

Registered User.
Local time
Today, 15:40
Joined
Feb 24, 2010
Messages
199
To return you to the point of exit so the loop can continue with the next record.
Hmm, I've not messed with GoSub, it looks a lot like splitting into functions suggested above? the "next bit of code" I want to work on ends up down at the end of the function which seems out of sequence?
 

NauticalGent

Ignore List Poster Boy
Local time
Today, 10:40
Joined
Apr 27, 2015
Messages
6,341
Hmm, I've not messed with GoSub, it looks a lot like splitting into functions suggested above? the "next bit of code" I want to work on ends up down at the end of the function which seems out of sequence?
Very similar, main difference from what I can see is that it stays with in the current sub. The routine called with the GoSub needs to be AFTER any Exit Sub code so that it does not get run in everything is done correctly
 

Josef P.

Well-known member
Local time
Today, 16:40
Joined
Feb 2, 2023
Messages
826
Hehe, 1 thing as in, it's a function for picking stock, chunks of it aren't going to be used for anything else by other code or called from elsewhere etc
This is the public interface. What happens inside via private procedures does not affect the public interface.
The single-responsibility principle has a special advantage: you can concentrate on one thing when programming.

An extreme example:
https://github.com/AccessCodeLib/AccessCodeLib/blob/master/data/SqlTools.cls .. Method: BuildCriteria
This procedure is a monster as it is. Imagine if the code of the auxiliary procedures used were also in it. => unreadable - unmaintainable

A video by Philipp fitting to the topic:
 
Last edited:

Pat Hartman

Super Moderator
Staff member
Local time
Today, 10:40
Joined
Feb 19, 2002
Messages
43,275
This one of the few situations where GoSub/Return is perfect. If there is a better way, I would be very interested...
In complex procedures, I use GoSub's for readability. Rather than having lots of lines of code and nesting inside a loop, I break up the process into pieces. In your case it allows you to check the value and then either run the code or not but continue the loop. It is very helpful for overall understanding if you can see the entire scope of a loop without scrolling and judicious use of GoSubs facilitates that.

I use GoSub's in this situation rather than calls to separate subs or functions because it allows the procedures to all share the same variables. If you call other procedures, you either have to declare public variables, which I avoid if possible, or pass in variables which gets messy and you have to worry about how to get return values if needed.

Code:
Do Until ...
    If x then
        GoSub xx
    end If
    GoSub bb
    GoSub cc
    Select Case z
        case 1
            GoSub s1
        Case 2
            GoSub s2
        Case Else
            GoSubsOther
    End Select
Loop
 
Last edited:

NauticalGent

Ignore List Poster Boy
Local time
Today, 10:40
Joined
Apr 27, 2015
Messages
6,341
It is very helpful for understanding if you can see the entire scope of a loop without scrolling and judicious use of GoSubs facilitates that.
GoSubs are not that popular with some very respected Devs on this and other forums. I imagine they are right, but I do not really see much difference between them and calling another sub...

Not advocating their use, if there IS a downside, I am all ears(eyes)
 

Users who are viewing this thread

Top Bottom