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))
Code: Select all
if %x% equ 1 echo ONE
if %x% equ 2 echo TWO
if %x% equ 3 echo THREE
----------------------------
#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
----------------------------
#3 12 Feb 2021 06:51
Rekrul
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:
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.
I've had many problems with delayed expansion. My nested line seems to work OK though.T3RRY wrote:
The most common issue with using Nested if Conditions is the delayed expansion "trap" that catches so many new scripters out;
I was previously warned (I forget if it was here or elsewhere) that arrays were unreliable in Batch.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.
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
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've had many problems with delayed expansion. My nested line seems to work OK though.
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.Rekrul wrote:
I was previously warned (I forget if it was here or elsewhere) that arrays were unreliable in Batch.
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
When dealing with very small data sets only once or twice in a script, there is little benefit to using an array.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 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
I like how you post about readability and maintainability and then post that.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
----------------------------
#6 19 Feb 2021 07:43
Rekrul
Or when reading filenames, which may contain them, or when reading lines from a text file.T3RRY wrote:
The only time delayed expansion is a real issue is when dealing with input data that may contain exclamation marks,
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.T3RRY wrote:
which can be handled by toggling Expansion state and testing the input data.
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.
I'll be honest, I don't understand any of that.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 often struggle with simple stuff.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 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
That is a form of input.Rekrul wrote:
Or when reading filenames, which may contain them, or when reading lines from a text file.T3RRY wrote:
The only time delayed expansion is a real issue is when dealing with input data that may contain exclamation marks,
Rekrul wrote:
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.T3RRY wrote:
which can be handled by toggling Expansion state and testing the input data.
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
)
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
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?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.
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.