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

Problem 7

配列?

素数を求めるといえば、ふつうはエラトステネスのふるいですね。それには配列を使わなければなりません。しかし、バッチファイルに配列はありません。ではどうすればよいでしょう。こんなのはどうでしょうか。

@echo off

for /L %%i in (1, 1, 10) do set /a a%%i = %%i
echo %a1% %a2% %a3% %a4% %a5% %a6% %a7% %a8% %a9% %a10%
1 2 3 4 5 6 7 8 9 10

これで確かにa1からa10に値が代入されています。しかしこれではインデックスを変数にしては値が取り出せそうにありませんね。
実は、これは次のようにすれば可能です。

for /L %%i in (1, 1, 10) do set /a s += "a%%i"
echo %s%
55

"a%%i"ですが、まず引用符の中が展開されて"a1"などとなります。そして、set /aの中では引用符の中が評価されて、%a1%と同等になります。
注意しなければならないのは、set /aでしか値を取り出せないので、この配列もどきには実質整数しか格納できないということです。
以下では、2次元以上のことを考えて、

a_0, a_1

としましょう。そして配列の大きさは、

set /a a.size = 2

などとすることにします。
さて、エラトステネスのふるいですが、準備が必要です。まず、最初に配列をすべて1で初期化する必要があります。C++とstd::fillと同じような関数を作りましょう。名前はvector_fillとします。注意しておきたいのは、ここではsetlocalは使えないことです。配列の要素に代入しているのではなく、実際には新たな変数を作っているため、setlocalを使うとこの変数が外では使えないことになります。変数が他とかぶる可能性があるので、極力引数をそのまま使っています。ついでにデバッグのために配列の要素をすべて表示するprint_vectorも作っておきます。

@echo off

set /a a.size = 10
call :fill_vector a 1
call :print_vector a
exit /b 0

:fill_vector
    set /a max_index = "%1.size" - 1
    for /L %%i in (0, 1, %max_index%) do set /a %1_%%i = %2
    exit /b 0

:print_vector
    setlocal
    set vector=%1
    set /a size = "%vector%.size"
    echo %size%
    if %size% == 0 echo %2 & exit /b 0
    set /a e = "%vector%_0"
    set s=%e%
    set /a k = 1
    :loop_print_vector
        if %k% == %size% echo %s% & exit /b 0
        set /a e = "%vector%_%k%"
        set s=%s% %e%
        set /a k += 1
        goto :loop_print_vector
10
1 1 1 1 1 1 1 1 1 1