Page 1 of 1

best practice with many ifs, whiles...

Posted: Fri Feb 22, 2019 8:52 pm
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

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

Posted: Fri Feb 22, 2019 9:14 pm
by Tenaja
I copy and paste the condition:

Code: Select all

If a=1
  if b = 2
    ;
  endif ; b=2
endif ; a=1

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

Posted: Fri Feb 22, 2019 9:25 pm
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.

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

Posted: Sat Feb 23, 2019 9:46 am
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

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

Posted: Sat Feb 23, 2019 11:24 am
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

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

Posted: Sat Feb 23, 2019 11:58 am
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.

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

Posted: Sat Feb 23, 2019 1:27 pm
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

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

Posted: Sat Feb 23, 2019 11:26 pm
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

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

Posted: Sun Feb 24, 2019 12:57 am
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.

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

Posted: Sat Mar 02, 2019 11:30 pm
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.

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

Posted: Sat Mar 02, 2019 11:45 pm
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