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].
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