Batch function help?

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Message
Author
JWinslow23
Posts: 58
Joined: 17 Jun 2014 10:38

Batch function help?

#1 Post by JWinslow23 » 02 Jul 2014 15:30

Hello! I am trying to create a function in Batch for my first time, and it's not working so far, for some reason. Maybe it's because I tried to concatenate the strLen routine with another that I made up...I should try a separate call to strLen if I don't find anything else.

Here is the code for my function so far. It is supposed to calculate the Scrabble score of a phrase in a variable, and output the score to another variable.

Code: Select all

@echo off
set "myString=Hello"
call :scrabbleScore myString score
echo Phrase: %myString%
echo Score: %score%
pause >nul
exit /b

:scrabbleScore
setlocal enabledelayedexpansion
set "_=A!%~1!"
set #=0
for /l %%g in (12,-1,0) do (set /a "#|=1<<%%g"
for %%h in (!#!) do if "!_:~%%h,1!"=="" set /a "#&=~1<<%%g")
set "@=%1"
set "$=0"
for /l %%g in (1,1,%#%) do (
  for %%h in (A a E e I i L l N n O o R r S s T t U u) do if !%@%:~%%g,1!==%%h set /a $+=1
  for %%h in (D d G g) do if !%@%:~%%g,1!==%%h set /a $+=2
  for %%h in (B b C c M m P p) do if !%@%:~%%g,1!==%%h set /a $+=3
  for %%h in (F f H h V v W w Y y) do if !%@%:~%%g,1!==%%h set /a $+=4
  for %%h in (K k) do if !%@%:~%%g,1!==%%h set /a $+=5
  for %%h in (J j X x) do if !%@%:~%%g,1!==%%h set /a $+=8
  for %%h in (Q q Z z) do if !%@%:~%%g,1!==%%h set /a $+=10
)
endlocal
if "%~2" neq "" set %~2=%$%
exit /b
This test case is supposed to output:

Code: Select all

Phrase: Hello
Score: 8
Any help would be greatly appreciated, thanks!

ShadowThief
Expert
Posts: 1166
Joined: 06 Sep 2013 21:28
Location: Virginia, United States

Re: Batch function help?

#2 Post by ShadowThief » 02 Jul 2014 15:40

First and foremost, if you're going to use vague, seemingly random single characters for variables, you're gonna have a bad time.
Second, %score% has no value because it only exists inside of delayedexpansion. Put the setlocal enabledelayedexpansion right after the @echo off.

Squashman
Expert
Posts: 4486
Joined: 23 Dec 2011 13:59

Re: Batch function help?

#3 Post by Squashman » 02 Jul 2014 16:08

Haven't figured out why you are getting this error

Code: Select all

1!==%h was unexpected at this time.

But I do see two flaws in your initial logic.
Your @ variable should be set like this

Code: Select all

set "@=!%~1!"

Your FOR /L should start with Zero because when you want to substring with the SET command the first position is 0. It is basically an offset.

Code: Select all

for /l %%g in (0,1,%#%) do (

Just an example of doing substrings.

Code: Select all

C:\BatchFiles\scrabble>set mystring=hello

C:\BatchFiles\scrabble>set var=%mystring:~0,1%

C:\BatchFiles\scrabble>echo %var%
h

Then you would also need to subtract one from your length so that it only runs your loop the correct amount of times for the string length.

einstein1969
Expert
Posts: 960
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: Batch function help?

#4 Post by einstein1969 » 02 Jul 2014 16:20

There are 3 problem

1) or use double quote surrounding "!%@%:~%%g,1!"=="%%h" or escape
2) The index of string start from 0 to len-1
3) for bypass endlocal barrier the set must be on the same line or using ().

Code: Select all

@echo off
set "myString=Hello"
call :scrabbleScore myString score
echo Phrase: %myString%
echo Score: %score%
pause >nul
exit /b

:scrabbleScore
setlocal enabledelayedexpansion
set "_=A!%~1!"
set #=0
for /l %%g in (12,-1,0) do (set /a "#|=1<<%%g"
for %%h in (!#!) do if "!_:~%%h,1!"=="" set /a "#&=~1<<%%g")
set "@=%1"
set "$=0"
set /a Len=#-1
for /l %%g in (0,1,%Len%) do (
  for %%h in (A a E e I i L l N n O o R r S s T t U u) do if "!%@%:~%%g,1!"=="%%h" set /a $+=1
  for %%h in (D d G g) do if "!%@%:~%%g,1!"=="%%h" set /a $+=2
  for %%h in (B b C c M m P p) do if "!%@%:~%%g,1!"=="%%h" set /a $+=3
  for %%h in (F f H h V v W w Y y) do if "!%@%:~%%g,1!"=="%%h" set /a $+=4
  for %%h in (K k) do if "!%@%:~%%g,1!"=="%%h" set /a $+=5
  for %%h in (J j X x) do if "!%@%:~%%g,1!"=="%%h" set /a $+=8
  for %%h in (Q q Z z) do if "!%@%:~%%g,1!"=="%%h" set /a $+=10
)
endlocal & if "%~2" neq "" set %~2=%$%
exit /b


einstein1969

JWinslow23
Posts: 58
Joined: 17 Jun 2014 10:38

Re: Batch function help?

#5 Post by JWinslow23 » 02 Jul 2014 16:56

Never mind, I found a solution. It works properly now. Maybe I could add this to the site's functions page...

Code: Select all

@echo off
call :scrabbleScore "This test case should return sixty-six points" score
echo Phrase: This test case should return sixty-six points
echo Score: %score%
echo(

set "myString=This phrase is supposed to be worth exactly one hundred twenty points in Scrabble"
call :scrabbleScoreVar myString score
echo Phrase: %myString%
echo Score: %score%

pause >nul
exit /b

:scrabbleScore phrase score -- Calculates and returns the Scrabble score of a phrase in quotes
::                          -- phrase [in]: phrase to calculate score of (must be in quotes)
::                          -- score [out]: variable name to store result to
if "%~2" neq "" set ?=%1&call :scrabbleScoreVar ? %~2
exit /b

:scrabbleScoreVar phrasevar score -- Calculates and returns the Scrabble score of a variable
::                                -- phrasevar [in]: variable name to calculate score of
::                                -- score [out]   : variable name to store result to
setlocal enabledelayedexpansion
set _=A!%~1!&set #=0&for /l %%g in (12,-1,0)do (set /a "#|=1<<%%g"
for %%h in (!#!)do if "!_:~%%h,1!"=="" set /a "#&=~1<<%%g")
set/a#-=1,$=0&set @=!%~1!&for /l %%g in (0,1,%#%)do (call set j=%%@:~%%g,1%%
for %%h in (A a E e I i L l N n O o R r S s T t U u)do if !j!==%%h set /a $+=1
for %%h in (D d G g)do if !j!==%%h set /a $+=2
for %%h in (B b C c M m P p)do if !j!==%%h set /a $+=3
for %%h in (F f H h V v W w Y y)do if !j!==%%h set /a $+=4
for %%h in (K k)do if !j!==%%h set /a $+=5
for %%h in (J j X x)do if !j!==%%h set /a $+=8
for %%h in (Q q Z z)do if !j!==%%h set /a $+=10)
endlocal&if "%~2" neq "" set %~2=%$%
exit /b
This includes code to calculate the score of a variable and of quoted text, plus two test cases.

Whaddaya think? :D

EDIT: Made it much faster and shorter.

einstein1969
Expert
Posts: 960
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: Batch function help?

#6 Post by einstein1969 » 02 Jul 2014 18:32

You're doing great! Keep it up! :wink:

einstein1969

Squashman
Expert
Posts: 4486
Joined: 23 Dec 2011 13:59

Re: Batch function help?

#7 Post by Squashman » 02 Jul 2014 18:34

JWinslow23 wrote:Whaddaya think? :D

As mentioned earlier by Shadowthief most people do not like this coding style.
I try to make my code as readable as possible using indentation and descriptive variables. I don't mind the use of symbols as variables as long as the code is indented and readable. When you start using symbols as variables instead of Alphanumeric names it makes it very hard for someone to come along behind you and understand how to use the code or even fix it in the future or add enhancements to the code if they are not very experienced with the scripting language.

JWinslow23
Posts: 58
Joined: 17 Jun 2014 10:38

Re: Batch function help?

#8 Post by JWinslow23 » 02 Jul 2014 18:38

Squashman: Sorry, it's just my style is to make my code tiny. I'll make a more readable version in a while.
EDIT: I believe that this, with a few modifications, could go on the Functions page:

Code: Select all

@echo off
call :scrabbleScore "This test case should return sixty-six points" score
echo Phrase: This test case should return sixty-six points
echo Score: %score%
echo(

set "myString=This phrase is supposed to be worth exactly one hundred twenty points in Scrabble"
call :scrabbleScoreVar myString score
echo Phrase: %myString%
echo Score: %score%

pause >nul
exit /b

:scrabbleScore phrase score -- Calculates and returns the Scrabble score of a phrase in quotes
::                          -- phrase [in]: phrase to calculate score of (must be in quotes)
::                          -- score [out]: variable name to store result to
if "%~2" neq "" (
    set ?=%1
    call :scrabbleScoreVar ? %~2
)
exit /b

:scrabbleScoreVar phrasevar score -- Calculates and returns the Scrabble score of a variable
::                                -- phrasevar [in]: variable name to calculate score of
::                                -- score [out]   : variable name to store result to
setlocal enabledelayedexpansion
set "str=A!%~1!"
set "len=0"
for /l %%g in (12,-1,0) do (
    set /a "len|=1<<%%g"
    for %%h in (!len!) do if "!str:~%%h,1!"=="" set /a "len&=~1<<%%g"
)
set /a len-=1
set #=0
set str=!%~1!
for /l %%g in (0,1,%len%) do (
    call set sub=%%str:~%%g,1%%
    for %%h in (A a E e I i L l N n O o R r S s T t U u) do if !sub!==%%h set /a #+=1
    for %%h in (D d G g) do if !sub!==%%h set /a #+=2
    for %%h in (B b C c M m P p) do if !sub!==%%h set /a #+=3
    for %%h in (F f H h V v W w Y y) do if !sub!==%%h set /a #+=4
    for %%h in (K k) do if !sub!==%%h set /a #+=5
    for %%h in (J j X x) do if !sub!==%%h set /a #+=8
    for %%h in (Q q Z z) do if !sub!==%%h set /a #+=10
)
endlocal & if "%~2" neq "" set %~2=%#%
exit /b

einstein1969
Expert
Posts: 960
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: Batch function help?

#9 Post by einstein1969 » 02 Jul 2014 19:08

For faster execution read this CALL me, or better avoid call

In dos batch should always make two versions:

1) A more readable version with variable names understandable.
Even indentation and spacing are important.

2) The second version is the version that instead it goes fast. In this case the spaces, length of variables affect the speed. This version can not be easily readable.

Looks at Ed Dyreen expert's posts. (dubbed "too complex"). :shock: example A special visual effect code: The Matrix

or jeb's cat:

Code: Select all

@echo off
setlocal EnableDelayedExpansion
set LF=^


set ^"-=:-)^
^% ;-) %^
%  8-( %^^
%lf:^=!LF!%!LF:^
&"=<&^|>!"
set ^"+=Why?This echo It works^^^^^^^^^^!"
(%-%%+%)


einstein1969

Aacini
Expert
Posts: 1913
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: Batch function help?

#10 Post by Aacini » 02 Jul 2014 19:54

Code: Select all

@echo off
setlocal EnableDelayedExpansion

rem Define the array of values per letter
for %%a in (A E I L N O R S T U) do set value[%%a]=1
for %%a in (D G) do set value[%%a]=2
for %%a in (B C M P) do set value[%%a]=3
for %%a in (F H V W Y) do set value[%%a]=4
set value[K]=5
for %%a in (J X) do set value[%%a]=8
for %%a in (Q Z) do set value[%%a]=10

call :scrabbleScore "This test case should return sixty-six points" score
echo Phrase: This test case should return sixty-six points
echo Score: %score%
echo/

set "myString=This phrase is supposed to be worth exactly one hundred twenty points in Scrabble"
call :scrabbleScoreVar myString score
echo Phrase: %myString%
echo Score: %score%

pause >nul
exit /b

:scrabbleScore phrase score -- Calculates and returns the Scrabble score of a phrase in quotes
::                          -- phrase [in]: phrase to calculate score of (must be in quotes)
::                          -- score [out]: variable name to store result to
if "%~2" equ "" exit /B
set "phrase=%1"
goto entryPoint

:scrabbleScoreVar phrasevar score -- Calculates and returns the Scrabble score of a variable
::                                -- phrasevar [in]: variable name to calculate score of
::                                -- score [out]   : variable name to store result to
if "%~2" equ "" exit /B
set "phrase=!%~1!"

:entryPoint
set %2=0
for /F "delims=" %%a in ('cmd /U /C echo %phrase%^| find /V ""') do if "%%a" neq " " set /A "%2+=value[%%a]"
exit /b


Antonio

JWinslow23
Posts: 58
Joined: 17 Jun 2014 10:38

Re: Batch function help?

#11 Post by JWinslow23 » 02 Jul 2014 21:05

Bravo there, Aacini, as usual. This seems like a better approach to store the values into an array and call them for each character. However, does this work for uppercase as well as lowercase? If it does, then I am definitely using it.

ShadowThief
Expert
Posts: 1166
Joined: 06 Sep 2013 21:28
Location: Virginia, United States

Re: Batch function help?

#12 Post by ShadowThief » 02 Jul 2014 22:36

JWinslow23 wrote:Bravo there, Aacini, as usual. This seems like a better approach to store the values into an array and call them for each character. However, does this work for uppercase as well as lowercase? If it does, then I am definitely using it.

Only as long as you want the uppercase and lowercase values to be identical; batch variables are not case sensitive.

foxidrive
Expert
Posts: 6031
Joined: 10 Feb 2012 02:20

Re: Batch function help?

#13 Post by foxidrive » 03 Jul 2014 00:46

The if command has the /i case insensitive switch which is useful.

Code: Select all

for %%h in (A E I L N O R S T U) do if /i !j!==%%h set /a $+=1
for %%h in (D G) do if /i !j!==%%h set /a $+=2
for %%h in (B C M P) do if /i !j!==%%h set /a $+=3
for %%h in (F H V W Y) do if /i !j!==%%h set /a $+=4
for %%h in (K) do if /i !j!==%%h set /a $+=5
for %%h in (J X) do if /i !j!==%%h set /a $+=8
for %%h in (Q Z) do if /i !j!==%%h set /a $+=10)


instead of

Code: Select all

for %%h in (A a E e I i L l N n O o R r S s T t U u)do if !j!==%%h set /a $+=1
for %%h in (D d G g)do if !j!==%%h set /a $+=2
for %%h in (B b C c M m P p)do if !j!==%%h set /a $+=3
for %%h in (F f H h V v W w Y y)do if !j!==%%h set /a $+=4
for %%h in (K k)do if !j!==%%h set /a $+=5
for %%h in (J j X x)do if !j!==%%h set /a $+=8
for %%h in (Q q Z z)do if !j!==%%h set /a $+=10)

JWinslow23
Posts: 58
Joined: 17 Jun 2014 10:38

Re: Batch function help?

#14 Post by JWinslow23 » 03 Jul 2014 19:26

foxidrive wrote:The if command has the /i case insensitive switch which is useful.

Code: Select all

for %%h in (A E I L N O R S T U) do if /i !j!==%%h set /a $+=1
for %%h in (D G) do if /i !j!==%%h set /a $+=2
for %%h in (B C M P) do if /i !j!==%%h set /a $+=3
for %%h in (F H V W Y) do if /i !j!==%%h set /a $+=4
for %%h in (K) do if /i !j!==%%h set /a $+=5
for %%h in (J X) do if /i !j!==%%h set /a $+=8
for %%h in (Q Z) do if /i !j!==%%h set /a $+=10)


instead of

Code: Select all

for %%h in (A a E e I i L l N n O o R r S s T t U u)do if !j!==%%h set /a $+=1
for %%h in (D d G g)do if !j!==%%h set /a $+=2
for %%h in (B b C c M m P p)do if !j!==%%h set /a $+=3
for %%h in (F f H h V v W w Y y)do if !j!==%%h set /a $+=4
for %%h in (K k)do if !j!==%%h set /a $+=5
for %%h in (J j X x)do if !j!==%%h set /a $+=8
for %%h in (Q q Z z)do if !j!==%%h set /a $+=10)

Noted. That could be useful. :)

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: Batch function help?

#15 Post by dbenham » 04 Jul 2014 08:47

einstein1969 wrote:
Squashman wrote:
JWinslow23 wrote:Whaddaya think? :D

As mentioned earlier by Shadowthief most people do not like this coding style.
I try to make my code as readable as possible using indentation and descriptive variables. I don't mind the use of symbols as variables as long as the code is indented and readable. When you start using symbols as variables instead of Alphanumeric names it makes it very hard for someone to come along behind you and understand how to use the code or even fix it in the future or add enhancements to the code if they are not very experienced with the scripting language.

In dos batch should always make two versions:

1) A more readable version with variable names understandable.
Even indentation and spacing are important.

2) The second version is the version that instead it goes fast. In this case the spaces, length of variables affect the speed. This version can not be easily readable.


I find that advice unworkable. It is hard enough to debug and maintain one version. How can you be sure that your second optimized version is functionally identical to the readable version? Each version would need to be independently tested every time any change is made.

I strongly agree with Squashman.

I understand that length of code can impact performance, but that effect is usually minimal compared to other batch bottlenecks. (I'm assuming the script is on a reasonably fast storage medium)

In every script I have worked on, I have always been able to achieve good, perfectly acceptable performance without resorting to short, unintelligible variable names, remark stripping, or indent/space stripping. Could I squeeze out a bit more performance by using those techniques? - Yes. But I've never found it worthwhile. And I hate reading code that does use those techniques.

I've written some significant game scripts that impress people with their performance, yet they all use sensible names, extensive documentation, and good indentation:

SNAKE.BAT 27k - viewtopic.php?f=3&t=4741

ADVENTURE.BAT 158k - viewtopic.php?f=3&t=4876


Dave Benham

Post Reply