best practice with many ifs, whiles...

Everything else that doesn't fall into one of the other PB categories.
miskox
User
User
Posts: 95
Joined: Sun Aug 27, 2017 7:37 pm
Location: Slovenia

best practice with many ifs, whiles...

Post by miskox »

Hello!

How do you know what IF goes with what ENDIF (WHILE, WEND...) if you have them many and a very loooong source?

Code: Select all

IF xxx
  IF yyy
     IF zzz
.
.
.
. many lines of code (more than few screens)
.
     ENDIF
      IF iii
.
.
.
      ENDIF
  ENDIF
ENDIF
Maybe adding a remark?

Code: Select all

IF xxx  ;IF1
  IF yyy    ;IF2
     IF zzz    ;IF3
.
.
.
. many lines of code (more than few screens)
.
     ENDIF   ;EIF3
      IF iii    ;IF4
.
.
.
      ENDIF  ;EIF4
  ENDIF   ;EIF2
ENDIF   ;EIF1
When writing code putting many IFs in the code and then knowing when to put ENDIF...

Thanks.
Saso
User avatar
Tenaja
Addict
Addict
Posts: 1949
Joined: Tue Nov 09, 2010 10:15 pm

Re: best practice with many ifs, whiles...

Post by Tenaja »

I copy and paste the condition:

Code: Select all

If a=1
  if b = 2
    ;
  endif ; b=2
endif ; a=1
User avatar
skywalk
Addict
Addict
Posts: 3999
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: best practice with many ifs, whiles...

Post by skywalk »

Use the IDE's folding and indent guidelines to locate column position.
And/or jump to top and bottom of your "if..else..endif" using the shortcut [Ctrl+K]. :wink:

Even better is a Feature Request to colorize each indent guideline differently.
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
Denis
Enthusiast
Enthusiast
Posts: 704
Joined: Fri Apr 25, 2003 5:10 pm
Location: Doubs - France

Re: best practice with many ifs, whiles...

Post by Denis »

May be like this

Code: Select all

            xxx           yyy         zzz          iii      "IF1"      "IF2"      "IF3"      "IF4"
Values       0             0           0            0       #False     #False     #False     #False
Values       0             0           0            1       #False     #False     #False     #False
Values       0             0           1            0       #False     #False     #False     #False
Values       0             0           1            1       #False     #False     #False     #False
Values       0             1           0            0       #False     #False     #False     #False
Values       0             1           0            1       #False     #False     #False     #False
Values       0             1           1            0       #False     #False     #False     #False
Values       0             1           1            1       #False     #False     #False     #False

Values       1             0           0            0       #True      #False     #False     #False
Values       1             0           0            1       #True      #False     #False     #False
Values       1             0           1            0       #True      #False     #False     #False
Values       1             0           1            1       #True      #False     #False     #False
Values       1             1           0            0       #True      #True      #False     #False
Values       1             1           0            1       #True      #True      #False     #True
Values       1             1           1            0       #True      #True      #True      #False
Values       1             1           1            1       #True      #True      #True      #True


Result

IF1 = xxx
IF2 = xxx And yyy           ==>  =  yyy And Bool(IF1)
IF3 = xxx And yyy And zzz   ==>  =  zzz And Bool(IF2)
IF4 = xxx And yyy And iii   ==>  =  iii And Bool(IF2)

and PB code

Code: Select all

If xxx
      IF1 = #True
EndIf   ;EIF1


If yyy And Bool(IF1)
      IF2 = #True
EndIf   ;EIF2

If zzz And Bool(IF2) ;IF3
      IF3 = #True
      ;.
      ; .
      ; .
      ; . many lines of code (more than few screens)
      ; .
EndIf   ;EIF3


If iii And Bool(IF2) ;IF4
      IF4 = #True
      ; .
      ; .
      ; .
EndIf  ;EIF4
and 4 loops to see the result

Code: Select all

Global NewList resultat$()
For xxx = 0 To 1
      For yyy = 0 To 1
            For zzz = 0 To 1
                  For iii = 0 To 1
                        
                        If xxx
                              IF1 = #True
                              AddElement(resultat$())
                              resultat$()= "IF1 = #True" + "    xxx = " + Str(xxx)  +  "    yyy = " + Str(yyy)  +  "    zzz = " + Str(zzz)  +  "    iii = " + Str(iii)
                        EndIf   ;EIF1
                        
                        
                        If yyy And Bool(IF1)
                              IF2 = #True
                              AddElement(resultat$())
                              resultat$()= "IF2 = #True" + "    xxx = " + Str(xxx)  +  "    yyy = " + Str(yyy)  +  "    zzz = " + Str(zzz)  +  "    iii = " + Str(iii)
                        EndIf   ;EIF2
                        
                        If zzz And Bool(IF2) ;IF3
                              IF3 = #True
                              AddElement(resultat$())
                              resultat$()= "IF3 = #True" + "    xxx = " + Str(xxx)  +  "    yyy = " + Str(yyy)  +  "    zzz = " + Str(zzz)  +  "    iii = " + Str(iii)
                              ;.
                              ; .
                              ; .
                              ; . many lines of code (more than few screens)
                              ; .
                        EndIf   ;EIF3
                        
                        
                        If iii And Bool(IF2) ;IF4
                              IF4 = #True
                              AddElement(resultat$())
                              resultat$()= "IF4 = #True" + "    xxx = " + Str(xxx)  +  "    yyy = " + Str(yyy)  +  "    zzz = " + Str(zzz)  +  "    iii = " + Str(iii)
                              ; .
                              ; .
                              ; .
                        EndIf  ;EIF4
                  Next
            Next
      Next
Next

SortList(resultat$(), #PB_Sort_Ascending)
ForEach resultat$()
      Debug resultat$()
Next
A+
Denis
User avatar
Josh
Addict
Addict
Posts: 1183
Joined: Sat Feb 13, 2010 3:45 pm

Re: best practice with many ifs, whiles...

Post by Josh »

In any case summarize in one line what belongs together. E.g. if a pointer has to be checked and is used only once:

Code: Select all

If *xxx And *xxx\yyy = 123
  ...
EndIf
sorry for my bad english
#NULL
Addict
Addict
Posts: 1440
Joined: Thu Aug 30, 2007 11:54 pm
Location: right here

Re: best practice with many ifs, whiles...

Post by #NULL »

Deep nesting is a code smell IMHO. Even without using procedures you can break it up into stages. It's easier to follow and you'll have each error handling closer to its respective check. In a procedure you could also use early return

Code: Select all

pointersOk = #False

If *x And *y And *z
  pointersOk = #True
Else
  Debug "error null pointer"
EndIf

valuesOk = #False

If pointersOk
  If *x\x <> 11 And *y\y <> 22 And *z\z <> 33
    valuesOk = #True
  Else
    Debug "error value"
  EndIf
EndIf

sumOk = #False

If valuesOk
  If *x\x + *y\y + *z\z = 100
    sumOk = #True
  Else
    Debug "error sum"
  EndIf
EndIf

If sumOk
  ...
EndIf
Some consider a function too long if it doesn't fit on a screen without scrolling.
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

Re: best practice with many ifs, whiles...

Post by Dude »

miskox wrote:How do you know what IF goes with what ENDIF (WHILE, WEND...) if you have them many and a very loooong source?
Lots of ways, such as:

- Folding the If/EndIf block (see below),
- Calling a procedure or macro instead between If/EndIf,
- Using IncludeFile between If/EndIf (ie. the relevant code is in a separate source file),
- Putting all fail conditions at the top so the loop exits at the top after If, and keeps the success code at the bottom before EndIf (but this really depends on what your If/EndIf does).

As mentioned above, you can just fold the If/EndIf bits that you don't need to see, using the ;{ and ;} tags. In the example below, I can fold (hide) the entire "If c=3" block with a single mouse click on the left of the IDE window at that line.

Code: Select all

If a=1
  If b = 2
    ;{
    If c = 3
      ...
    EndIf
    ;}
  EndIf
EndIf
miskox
User
User
Posts: 95
Joined: Sun Aug 27, 2017 7:37 pm
Location: Slovenia

Re: best practice with many ifs, whiles...

Post by miskox »

Thanks for the tips.

To avoid using GOTO statements source gets quite long... so I have problems which IF and ENDIF belong together.

So the ";{ " hint is great.

Thanks again.
Saso
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

Re: best practice with many ifs, whiles...

Post by Dude »

miskox wrote:I have problems which IF and ENDIF belong together
As skywalk said, you can enable "Indention guides" in the IDE Prefs ("Editor -> Indentation" section). This visually shows which If goes with which EndIf, but may not really help much in your situation where your code spills over several screens of code.
User avatar
Kurzer
Enthusiast
Enthusiast
Posts: 666
Joined: Sun Jun 11, 2006 12:07 am
Location: Near Hamburg

Re: best practice with many ifs, whiles...

Post by Kurzer »

Basically I think like #Null, but for certain cases I think a Goto is absolutely legitimate.

For example, in long procedures I can perform all checks in the procedure header (e.g. if several objects or memory blocks are allocated) and in case of an error jump to the exact location in the footer of the procedure that releases all the already created objects or memory blocks.

Pseudocode:

Code: Select all

Procedure foo()

*Object1 = AllocateMemory(some values)
If not *Object1 : Goto ErrorExit : EndIf

*Object2 = AllocateMemory(some values)
If not *Object2 : ErrorErrorReleaseObj1 : EndIf

*Myfont = LoadFont(...)
If not *MyFont : Goto ErrorReleaseObj2 : EndIf

*MyStruct = AllocateStructure(...)
If not *MyStruct : Goto ErrorReleaseFont : EndIf

[put here a very long code]

FreeStructure(*MyStruct)

ErrorReleaseFont:
FreeFont(*MyFont)

ErrorReleaseObj2:
FreeMemory(*Object2)

ErrorReleaseObj1:
FreeMemory(*Object1)

ErrorExit:
EndProcedure 
So, you need no nested If / EndIf levels.
Last edited by Kurzer on Sun Mar 03, 2019 7:49 am, edited 2 times in total.
PB 6.02 x64, OS: Win 7 Pro x64 & Win 11 x64, Desktopscaling: 125%, CPU: I7 6500, RAM: 16 GB, GPU: Intel Graphics HD 520, User age in 2024: 56y
"Happiness is a pet." | "Never run a changing system!"
User avatar
mk-soft
Always Here
Always Here
Posts: 5406
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: best practice with many ifs, whiles...

Post by mk-soft »

Perhaps this way...

Code: Select all


Macro do 
  Repeat
EndMacro

Macro exit_do
  Break
EndMacro

Macro end_do
  Until #True
EndMacro


Procedure foo()
  
  Protected *mem, *struct, font, image
  
  do
    *mem = AllocateMemory(100)
    If Not *mem
      exit_do
    EndIf
    
    *struct = AllocateStructure(String)
    If Not *struct
      exit_do
    EndIf
    
    font = 0
    If Not font
      exit_do
    EndIf
    
    image = CreateImage(#PB_Any, 32, 32)
    If Not image
      exit_do
    EndIf
    
    ; More code
  end_do
  
  If *mem
    FreeMemory(*mem)
  EndIf
  
  If *struct
    FreeStructure(*struct)
  EndIf
  
  If font
    FreeFont(font)
  EndIf
  
  If image
    FreeImage(image)
  EndIf
  
EndProcedure

My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
Post Reply