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 で教えていただきました。感謝です!
Tupleの配列と、TupleのInputRangeの違いです。foreachで回せる型は、配列と連想配列とopApplyのあるクラスや構造体とRange(と実はdelegate)があるんです。それぞれで2つの変数にした場合に格納されるものが違うんです。
— SHOO C94でお買い上げの皆様、ありがとうございました!! (@mono_shoo) August 11, 2018
cartesianProductは、ドキュメントに"Returns: A forward range of std.typecons.Tuple..." と書かれているようにRangeを返します。(ForwardRangeはInputRangeの仲間)
— SHOO C94でお買い上げの皆様、ありがとうございました!! (@mono_shoo) August 11, 2018
配列を 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