IF/ELSE vs. multiple IFs?

Microsoft Windows
Post Reply
User avatar
MigrationUser
Posts: 336
Joined: 2021-Jul-12, 1:37 pm
Contact:

IF/ELSE vs. multiple IFs?

Post by MigrationUser »

11 Feb 2021 09:03
Rekrul


When testing multiple conditions, is there any advantage to nesting multiple IF comparisons using ELSE, rather than simply using multiple IF lines?

In other words, is there any advantage to using this;

Code: Select all

if x equ %1% (echo ONE) else (if %x% equ 2 (echo TWO) else (if %x% equ 3 echo THREE))
Over this;

Code: Select all

if %x% equ 1 echo ONE
if %x% equ 2 echo TWO
if %x% equ 3 echo THREE
I suppose the first would be marginally faster if x happened to be 1 or 2, since it wouldn't execute the rest of the line, whereas the second one would always execute all three, but unless that section of the script was being executed thousands of times, I can't imagine the difference being enough to matter.

----------------------------

#2 11 Feb 2021 22:52
T3RRY


For readabilities and maintenance's sake, I'd not recommend same line chaining of If else statements except perhaps in the definition macro functions provided they don't require too long a line.
The most common issue with using Nested if Conditions is the delayed expansion "trap" that catches so many new scripters out; Using subsequent If statements outside of command blocks avoids the need to use delayed expansion, however it's not often that's necessary.

Generally, for the type of conditional execution you've exampled, and especially when there's a large number of tests to perform, Definition of arrays and expansion of the content using the relevent index is far more efficient, especially when the actions are repetative.
A script that Illustrates the use of [where batch is concerned] advanced conditional execution through the use of Arrays is my 48 line Tic Tac toe.

Out of curiosity I've searched out numerous other attempts at Tic Tac Toe using batch. I've found one so far that uses the majority of the same constructs, yet still comes in at a whopping 245 lines.

Where your example is concerned, you could do the following:

Code: Select all

 @Echo off
  Set "Def.Array=For %%n in (1 2)Do if %%n==2 ((If "!#$A!"=="" (Set "#$A=1"))&(For %%G in (!elements!)Do (Set "$A[!#$A!]=%%~G"&Set /A "#$A+=1")))Else Set elements="
 Setlocal EnableDelayedExpansion
  %Def.Array:$A=Number%One Two Three Four
  Echo Enter a Number: 1 2 3 4
  For /F "Delims=" %%i in ('Choice /N /C:1234')Do Echo/!Number[%%i]!
 Endlocal
Last edited by T3RRY (11 Feb 2021 23:30)

----------------------------

#3 12 Feb 2021 06:51
Rekrul
T3RRY wrote:

For readabilities and maintenance's sake, I'd not recommend same line chaining of If else statements except perhaps in the definition macro functions provided they don't require too long a line.
On the one hand, using multiple lines is more readable, but on the other hand, I like that all the relevant commands are handled in one line.
T3RRY wrote:

The most common issue with using Nested if Conditions is the delayed expansion "trap" that catches so many new scripters out;
I've had many problems with delayed expansion. My nested line seems to work OK though.
T3RRY wrote:

Generally, for the type of conditional execution you've exampled, and especially when there's a large number of tests to perform, Definition of arrays and expansion of the content using the relevent index is far more efficient, especially when the actions are repetative.
I was previously warned (I forget if it was here or elsewhere) that arrays were unreliable in Batch.

Generally, I use such lines to cycle values in a variable, 1 > 2 > 3 > 1, etc. I often pair that with a dynamic menu using the Choice command, so that the user only has to press a key to cycle the value, and by clearing the window and conditionally echoing the relevant lines, it looks like the menu is just toggling those lines in real time. It's a simple trick, but it looks slick. smile

Although in truth, I usually only have a need to cycle between two values, usually representing on and off.

----------------------------

#4 12 Feb 2021 07:34
T3RRY
Rekrul wrote:

I've had many problems with delayed expansion. My nested line seems to work OK though.
The only time delayed expansion is a real issue is when dealing with input data that may contain exclamation marks, which can be handled by toggling Expansion state and testing the input data.
Rekrul wrote:

I was previously warned (I forget if it was here or elsewhere) that arrays were unreliable in Batch.
Arrays in batch are not 'true' arrays. They are simply variables that use a naming convention to asociate a set of values in a way that lets different values be accessed based on the index string that differentiates one array element from another.

Delayed expansion is not the only way to expand array value, however it is the faster performing method.
The time taken to expand an array variable will always be less than the time taken to perform a series of condition tests. This is especially valuable for animations or games where you want to minimise the overhead as much as possible to effect a fast frame rate.

Using For loops of commands that get the index value can also speed things along.
Say you want to access a rabdom index value in a 1 indexed array with for elements:

Code: Select all

@Echo off
  Set "Def.Array=For %%n in (1 2)Do if %%n==2 ((If "!#$A!"=="" (Set "#$A=1"))&(For %%G in (!elements!)Do (Set "$A[!#$A!]=%%~G"&Set /A "#$A+=1")))Else Set elements="
 Setlocal EnableDelayedExpansion
  %Def.Array:$A=Number%One Two Three Four
  Echo Enter a Number: 1 2 3 4
  For /F "Delims=" %%i in ('Choice /N /C:1234')Do Echo/!Number[%%i]!
rem echo random array element
  For /F "Delims=" %%i in ('Set /A !random! %%4 + 1')Do Echo(!Number[%%i]!
 Endlocal
Rekrul wrote:

Generally, I use such lines to cycle values in a variable, 1 > 2 > 3 > 1, etc. I often pair that with a dynamic menu using the Choice command, so that the user only has to press a key to cycle the value, and by clearing the window and conditionally echoing the relevant lines, it looks like the menu is just toggling those lines in real time. It's a simple trick, but it looks slick. :)
When dealing with very small data sets only once or twice in a script, there is little benefit to using an array.
When performing repetative actions over medium to large data sets, the efficiency savings of an array as opposed to countless if conditions is enormous, not just in processing speed, but also in how quickly you can write the script.

Last edited by T3RRY (12 Feb 2021 08:05)

----------------------------

#5 13 Feb 2021 04:46
Shadow Thief
T3RRY wrote:

For readabilities and maintenance's sake, I'd not recommend same line chaining of If else statements except perhaps in the definition macro functions provided they don't require too long a line.
The most common issue with using Nested if Conditions is the delayed expansion "trap" that catches so many new scripters out; Using subsequent If statements outside of command blocks avoids the need to use delayed expansion, however it's not often that's necessary.

Generally, for the type of conditional execution you've exampled, and especially when there's a large number of tests to perform, Definition of arrays and expansion of the content using the relevent index is far more efficient, especially when the actions are repetative.
A script that Illustrates the use of [where batch is concerned] advanced conditional execution through the use of Arrays is my 48 line Tic Tac toe.

Out of curiosity I've searched out numerous other attempts at Tic Tac Toe using batch. I've found one so far that uses the majority of the same constructs, yet still comes in at a whopping 245 lines.

Where your example is concerned, you could do the following:

Code: Select all

     @Echo off
      Set "Def.Array=For %%n in (1 2)Do if %%n==2 ((If "!#$A!"=="" (Set "#$A=1"))&(For %%G in (!elements!)Do (Set "$A[!#$A!]=%%~G"&Set /A "#$A+=1")))Else Set elements="
     Setlocal EnableDelayedExpansion
      %Def.Array:$A=Number%One Two Three Four
      Echo Enter a Number: 1 2 3 4
      For /F "Delims=" %%i in ('Choice /N /C:1234')Do Echo/!Number[%%i]!
     Endlocal
I like how you post about readability and maintainability and then post that.

----------------------------

#6 19 Feb 2021 07:43
Rekrul
T3RRY wrote:

The only time delayed expansion is a real issue is when dealing with input data that may contain exclamation marks,
Or when reading filenames, which may contain them, or when reading lines from a text file.
T3RRY wrote:

which can be handled by toggling Expansion state and testing the input data.
I'm not sure what you mean here. It's my understanding that the only way to toggle Delayed Expansion is by using the Setlocal/Endlocal commands, which creates all sorts of problems for trying to pass variables to the rest of the script.

For example, if you have a For loop that reads lines from a file, you have to enable it AFTER reading a line, but then you need to disable it before the loop repeats again, but using Endlocal & Set doesn't work (that only works outside the loop). You have to include another For command to preserve the value. Which gets to be a pain in the butt if you have multiple values that need to be preserved.

Frankly, it would have solved so many problems if they had simply made the commands to enable/disable Delayed Expansion separate from the Local commands. Just enable/disable it for the entire scripts as needed.
T3RRY wrote:

Using For loops of commands that get the index value can also speed things along.
Say you want to access a rabdom index value in a 1 indexed array with for elements:
I'll be honest, I don't understand any of that.
T3RRY wrote:

When dealing with very small data sets only once or twice in a script, there is little benefit to using an array.
When performing repetative actions over medium to large data sets, the efficiency savings of an array as opposed to countless if conditions is enormous, not just in processing speed, but also in how quickly you can write the script.
I often struggle with simple stuff.

I recently created a script that will read in lines from a large SFV file and split it into multiple smaller ones, based on which lines pertain to which sub-directories. It could read and echo the lines to the screen perfectly, but when I told it to redirect them to a new file it gave me errors. I was just about to post here and ask for help when I found a post on another site mentioning Delayed Expansion. I changed the script to enable it and then it worked.

I probably should have included it from the start just in case of exclamation points in the filenames, but I usually start with the simplest example I can think of to test my theory before getting fancy, I figure why write a complex script full of various tricks and logic unless I have the core routine working properly.

----------------------------

#7 14 Apr 2021 04:37
T3RRY
Rekrul wrote:
T3RRY wrote:

The only time delayed expansion is a real issue is when dealing with input data that may contain exclamation marks,
Or when reading filenames, which may contain them, or when reading lines from a text file.
That is a form of input.
Rekrul wrote:
T3RRY wrote:

which can be handled by toggling Expansion state and testing the input data.
I'm not sure what you mean here. It's my understanding that the only way to toggle Delayed Expansion is by using the Setlocal/Endlocal commands, which creates all sorts of problems for trying to pass variables to the rest of the script.

For example, if you have a For loop that reads lines from a file, you have to enable it AFTER reading a line, but then you need to disable it before the loop repeats again, but using Endlocal & Set doesn't work (that only works outside the loop). You have to include another For command to preserve the value. Which gets to be a pain in the butt if you have multiple values that need to be preserved.

Frankly, it would have solved so many problems if they had simply made the commands to enable/disable Delayed Expansion separate from the Local commands. Just enable/disable it for the entire scripts as needed.

Contrary to your belief above, Delayed expansion can be enabled during a for loop and ended via endlocal. That is toggling as I mentioned previously.

A simple example:

Code: Select all

 Setlocal DISableDelayedExpansion
 For %%n in (1 2)Do (
  Set "line=Line %%n!"
  Setlocal ENableDelayedExpansion
  Echo(!line!
  Endlocal
 )
If you need to preserve variables across the endlocal, it is better to call a function and use tunneling to return any variables required:

Code: Select all

@Echo off
 Setlocal DISableDelayedExpansion

 For %%n in (1 2)Do (
  Set "line[%%n]=Line %%n!"
  Call :Foo line[%%n]
  Setlocal ENableDelayedExpansion
  Echo(!line[%%n]!
  Endlocal
 )
 Set Line[
 Pause
Goto :Eof

:Foo
 Setlocal ENableDelayedExpansion
 Set ^"TmpStr=!%~1!"
 Set ^"TmpStr=!TmpStr:1=One!"
 For %%v in ("!%~1!")Do Endlocal & (
  Set "%~1[Orig]=%%~v"
  Set "%~1=%TmpStr%"
 )
Goto :Eof
----------------------------

#8 03 May 2021 06:50
Rekrul
T3RRY wrote:

Contrary to your belief above, Delayed expansion can be enabled during a for loop and ended via endlocal. That is toggling as I mentioned previously.
I know this is a very late reply, but can anyone give me a single, practical reason for why the command to enable Delayed Expansion is tied to the Setlocal command?

You can use the Setlocal command on its own, but if you want to use Delayed Expansion, you're required to use Setlocal, and then you have to use Endlocal to toggle it off. Which then requires you to jump through hoops to preserve any variables defined or changed within the local commands.

Can someone offer a coherent reason for why the developers didn't just allow you to use Enabledelayedexpansion and Disabledelayedexpansion on their own to toggle the state of DE on/off without having to use the Setlocal/Endlocal commands?

As far as I can see, tying the two together offers absolutely no benefits and has a bunch of drawbacks.
Post Reply