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

遅延展開

絶望的に遅い状況をどうしたらよいでしょう。ふつうはforの中では変えた変数の値を使えないのですが、実はおまじないでこれができるようになるのです。例えばこれは恐らく意図通りに動きません。

set /a s = 0
for /L %%k in (1, 1, 10) do (
    set /a s = %s% * 2 + %%k
)
echo %s%

%s%はforが実行される前に評価されるので、最後は、set /a s = 0 * 2 + 10なので10が表示されます。しかし、こうするとループが回っているときに評価されます。

setlocal enabledelayedexpansion
set /a s = 0
for /L %%k in (1, 1, 10) do (
    set /a s = !s! * 2 + %%k
)
echo %s%

setlocal enabledelayedexpansionと指定して、forやifの中で%の代わりに!で囲うと実行時に評価されます。
従来どおりだと、forは使えず代わりにgotoを使うことになります。そうすると非常に遅くなります。

@echo off

setlocal
set /a N = 20
set /a M = 1000
for /L %%i in (1, 1, %M%) do call :test %N%
echo %ERRORLEVEL%
exit /b 0

:test
    setlocal
    set /a s = 0
    set /a k = 1
    :loop_test
        set /a s = %s% * 2 + %k%
        set /a k += 1
        if %k% LEQ %1 goto loop_test
    
    exit /b %s%

これを、こう変えると8倍速くなりました。

:test
    setlocal
    set /a s = 0
    for /L %%k in (1, 1, %1) do (
        set /a s = !s! * 2 + %%k
    )
    exit /b %s%