9
\$\begingroup\$

I've never done a FizzBuzz before, so I decided I would. Please let me know what I could have done better. This is ran via a .bat file in Windows.

  @echo off
  set loopStart=1
  set loopEnd=100
  set /a i=%loopStart% - 1  
:LoopStart

  if %i% == %loopEnd% goto :LoopEnd

  set /a i=%i% + 1
  set /a fizzbuzz=%i%%%15
  set /a buzz=%i%%%5
  set /a fizz=%i%%%3

  if %fizzbuzz% == 0 (
    echo FizzBuzz
  ) else if %buzz% == 0 (
    echo Buzz
  ) else if %fizz% == 0 (
    echo Fizz
  ) else (
    echo %i%
  )

  goto :LoopStart

:LoopEnd
  set loopStart=
  set loopEnd=
  set i=
  set fizzbuzz=
  set buzz=
  set fizz=
\$\endgroup\$

2 Answers 2

10
\$\begingroup\$

Use FOR /L to iterate a range of numbers - much faster than SET /A with GOTO loop.

Use SETLOCAL to localize variables so no need to undefine them. The EnableDelayedExpansion option does just that so that you can use !var! within a FOR loop.

A minor optimization - it is faster to combine multiple math computations into one SET /A.

@echo off
setlocal enableDelayedExpansion
for /l %%N in (1 1 100) do (
  set /a "fizzbuzz=%%N%%15, buzz=%%N%%5, fizz=%%N%%3"
  if !fizzbuzz! == 0 (
    echo FizzBuzz
  ) else if !buzz! == 0 (
    echo Buzz
  ) else if !fizz! == 0 (
    echo Fizz
  ) else echo %%N
)

You might consider parameterizing the start and end conditions of the loop.

@echo off
REM %1=start %2=end
setlocal enableDelayedExpansion
for /l %%N in (%1 1 %2) do (
  set /a "fizzbuzz=%%N%%15, buzz=%%N%%5, fizz=%%N%%3"
  if !fizzbuzz! == 0 (
    echo FizzBuzz
  ) else if !buzz! == 0 (
    echo Buzz
  ) else if !fizz! == 0 (
    echo Fizz
  ) else echo %%N
)

You could even parameterize the two divisors, but then a different algorithm is needed. Here is a fully parameterized solution that is efficient, though perhaps a bit obfuscated. I still use a modulo operation to test if divisible, but instead of an IF statement, I intentionally divide by zero to raise an error and trigger the conditional execution of the SET statement. Of course I redirect error messages to NUL to avoid unwanted error messages..

@echo off
REM %1=start %2=end %3=divisor1 %4=divisor2
setlocal enableDelayedExpansion
for /l %%N in (%1 1 %2) do (
  set "val="
  set /a "1/(%%N %% %3)" 2>nul || set "val=Fizz"
  set /a "1/(%%N %% %4)" 2>nul || set "val=!val!Buzz"
  if defined val (echo !val!) else echo %%N
)
\$\endgroup\$
1
  • \$\begingroup\$ This is great. I tried to find the proper way to loop via searching and the only method I could find was using GOTOs. I'm glad there's a better way. \$\endgroup\$
    – Daniel
    Dec 6, 2013 at 3:33
2
\$\begingroup\$

When there is a large set of input data for processing, but the different output results are just a small set (that is, several input data produce the same output result), the process is much faster if the set of different results is calculated just once in advance and then the appropriate result is just choosen in accordance with the input data:

@echo off
setlocal enableDelayedExpansion

rem Prepare in advance the 15 different results:
for /l %%N in (0,1,14) do (
   set /a "fizzbuzz=%%N%%15, buzz=%%N%%5, fizz=%%N%%3"
   if !fizzbuzz! == 0 (
      set result[%%N]=FizzBuzz
   ) else if !buzz! == 0 (
      set result[%%N]=Buzz
   ) else if !fizz! == 0 (
      set result[%%N]=Fizz
   )
)

rem Process input and get output at the fastest possible speed:
for /l %%N in (%1 1 %2) do (
   set /a "fizzbuzz=%%N%%15"
   if defined result[!fizzbuzz!] (
      for %%I in (!fizzbuzz!) do echo !result[%%I]!
   ) else (
      echo %%N
   )
)
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.