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

Problem 17


1ならoneなので長さ3、2ならtwoなので長さ3、3ならthreeなので長さ5などというのを連想配列にしておきたいところです。Pythonなら、

L = { 1: 3, 2: 3, 3: 5, ... }

などとします。しかし、これだとすぐにバグが発生しそうです。そこで、

s = { 1: "one", 2: "two", 3: "three", ... }

として、定義して長さを計算するほうが比較的バグが発生しにくいでしょう。
しかし、バッチファイルにはこのようなリテラルはありません。そこで、

call :set_length 1 one 2 two 3 three 4 four 5 five 6 six

と書いて、まず%2の長さを調べてL%1に格納します。そしてshiftを2回して同じことを繰り返します。
このようにして基本の数の長さを調べておけば、複合的な数も簡単に再帰で求められます。

@echo off

setlocal enabledelayedexpansion
set /a N = 1000
call :set_length 1 one 2 two 3 three 4 four 5 five 6 six
call :set_length 7 seven 8 eight 9 nine 10 ten 11 eleven
call :set_length 12 twelve 13 thirteen 14 fourteen
call :set_length 15 fifteen 16 sixteen 17 seventeen
call :set_length 18 eighteen 19 nineteen 20 twenty
call :set_length 30 thirty 40 forty 50 fifty 60 sixty
call :set_length 70 seventy 80 eighty 90 ninety
call :set_length 100 hundred 1000 thousand

set /a s = 0
for /L %%i in (1, 1, %N%) do (
    call :sum_length %%i
    set /a s += !ERRORLEVEL!
)
echo %s%
exit /b 0

:sum_length
    setlocal
    if %1 == 0 exit /b 0
    if %1 LEQ 20 (
        set /a n = "L%1"
        exit /b !n!
    )
    if %1 LSS 100 (
        set /a r = %1 %% 10
        set /a m = %1 - !r!
        call :sum_length !r!
        set /a n = !ERRORLEVEL! + "L!m!"
        exit /b !n!
    )
    if %1 LSS 1000 (
        set /a r = %1 %% 100
        set /a d = %1 / 100
        if !r! GTR 0 (
            call :sum_length !r!
            set /a n = "L!d!" + %L100% + 3 + !ERRORLEVEL!
        ) else (
            set /a n = "L!d!" + %L100%
        )
        exit /b !n!
    )
    if %1 == 1000 (
        set /a n = %L1% + %L1000%
        exit /b !n!
    )
    exit /b 0

:set_length
    :loop_set_length
        if "%1" == "" exit /b 0
        call :length %2
        set /a L%1 = %ERRORLEVEL%
        shift
        shift
        goto :loop_set_length

:length
    setlocal
    if "%1" == "" exit /b 0
    set s=%1
    call :length %s:~1%
    set /a m = 1 + %ERRORLEVEL%
    exit /b %m%