D言語: foreach の変数展開の仕組みが分からない

import std.experimental.all;

void main() {
    foreach (a; [10, 20, 30])
        writeln(a);
}

と書くと、配列の中身が foreach の変数 a に代入されて以下のように出力されます。

10
20
30

次に foreach の変数をひとつ追加します。 すると、その追加した変数には配列のインデックスが格納されます。

import std.experimental.all;

void main() {
    foreach (i, a; [10, 20, 30]) // 変数 i を追加した
        writeln(i, " ", a); // i はインデックス。a には配列の各要素
}

実行結果です。

0 10
1 20
2 30

さて、次は標準ライブラリの cartesianProduct 関数を使ったサンプルです。この関数は、引数で渡した配列の各要素の組み合わせを求める関数です。

import std.experimental.all;

void main() {
    foreach (a; cartesianProduct([1, 2], [3, 4]))
        writeln(a);
}

実行結果です。要素はタプルとしてまとめられます。

Tuple!(int, int)(1, 3)
Tuple!(int, int)(1, 4)
Tuple!(int, int)(2, 3)
Tuple!(int, int)(2, 4)

ここで foreach の変数をひとつ追加します。このとき、各変数にはタプルの各要素が代入されます。

import std.experimental.all;

void main() {
    foreach (a, b; cartesianProduct([1, 2], [3, 4]))
        writeln(a, " ", b);
}

実行結果です。

1 3
1 4
2 3
2 4

なるほど、各タプルの要素が展開されるのですね。 では、たとえば [tuple(1, 2), tuple(3, 4)] のような、タプルの配列があるとき foreach で各タプルの要素を変数で受けようとしても期待する結果にはなりません。

import std.experimental.all;

void main() {
    foreach (a, b; [tuple(1, 2), tuple(3, 4)])
        writeln(a, " ", b);
}

実行結果です。

0 Tuple!(int, int)(1, 2)
1 Tuple!(int, int)(3, 4)

上記のように、foeach の変数 a には配列のインデックスが格納されます。 配列と、cartesianProduct 関数のときで違いがでるのがよく分からないですね。

foreach の変数 a に 1 と 3 が代入されるようには書けないのですかね?

追記

twitter で教えていただきました。感謝です!

配列を zip でくるんで Input Range にするサンプル:

import std.experimental.all;

void main() {
    foreach (a, b; zip([tuple(1, 2), tuple(3, 4)])) {
        writeln(a, " ", b);
    }
}

実行結果です。

1 2
3 4