バッチファイルでProject Euler(5)

即時変数展開

forとifが使えるようになったので、Problem 1を題意どおりに書くことができます。ただし、バッチファイルにはandやorはないので、そこはifを2つ使うことになります。

@echo off

set /a N = 1000
set /a M = %N% - 1
set /a s = 0
for /L %%i in (1, 1, %M%) do (
    set /a r3 = %%i %% 3
    set /a r5 = %%i %% 5
    if %r3% == 0 (
        set /a s += %%i
    ) else (
        if %r5% == 0 (
            set /a s += %%i
        )
    )
)
echo %s%

ここで、3 %% 2だと3を2で割ったときの余りです。これを実行すると、

( の使い方が誤っています。

おや、なにやらエラーメッセージのようなものが出ていますね。しかしどこで出ているのかよくわかりません。こういうときは先頭行を

@echo on

とすると原因がわかることがあります。

>euler001d.bat

>set /a N = 1000

>set /a M = 1000 - 1

>set /a s = 0
( の使い方が誤っています。

>       if  == 0 (

どうやら、

    if %r3% == 0 (

の部分のようです。r3になにもセットされていないんですね。なぜこうなるかというと、ifやforの中の環境変数はそれらが実行される前に評価されるからです。この場合では、forの中にsetコマンドでr3に値をセットしているように見えますが、その前にr3が評価されてしまうので、この時点ではr3には何もセットされていないことになるのです。forやifはこのようなことがしばしば起きるので厄介です。

goto

このような場合で一番お手軽なのはgotoでループを回すことです。

@echo off

set /a N = 1000
set /a s = 0
set /a i = 1
:loop
    set /a r3 = %i% %% 3
    set /a r5 = %i% %% 5
    if %r3% == 0 (
        set /a s += %i%
    ) else (
        if %r5% == 0 (
            set /a s += %i%
        )
    )
    set /a i += 1
    if %i% LSS %N% goto :loop

echo %s%

単にgoto :labelで:labelに飛ぶというだけです。

再びfor

どうしてもforを使いたかったら、forの中でcallして、call先でsに足すのでしょうね。

@echo off

set /a N = 1000
set /a M = N - 1
set /a s = 0
for /L %%i in (1, 1, %M%) do call :sum %%i
echo %s%
exit /b 0

:sum
    call :is_valid %1
    if %ERRORLEVEL% == 1 set /a s += %1
    exit /b 0

:is_valid
    setlocal
    set /a r3 = %1 %% 3
    if %r3% == 0 exit /b 1
    set /a r5 = %1 %% 5
    if %r5% == 0 exit /b 1
    exit /b 0