多次元配列を操作する際、深いネスト作ってループすんのやめたい

高次元の配列に対する処理を書こうと思うと、その各次元に対応したループインデックスに対するfor分のネストがえらいことになる、そんな時あると思います。
F#でやるならどうしようかと考えて以下のようなコードを書いた。List.collectが便利ってことに気がついた。

let rec f (dim : int list) = 
    match dim with
    | [] -> [[]]
    | head::tail -> 
        (f tail) |> List.collect (fun x -> [for i in 0..head -> i::x]) 
f [1;2;3]

これで「0〜1」「0〜2」「0〜3」の全組み合わせを取得できる。

val f : dim:int list -> int list list
val it : int list list =  [[0; 0; 0]; [1; 0; 0]; [0; 1; 0]; [1; 1; 0]; [0; 2; 0]; [1; 2; 0]; [0; 0; 1];
   [1; 0; 1]; [0; 1; 1]; [1; 1; 1]; [0; 2; 1]; [1; 2; 1]; [0; 0; 2]; [1; 0; 2];
   [0; 1; 2]; [1; 1; 2]; [0; 2; 2]; [1; 2; 2]; [0; 0; 3]; [1; 0; 3]; [0; 1; 3];
   [1; 1; 3]; [0; 2; 3]; [1; 2; 3]]

使い勝手を考えると、listのlistじゃなくてarrayのseqの方がいいか。

let rec g (dim : int list) = 
    seq{
        match dim with
        | [] -> yield Array.empty
        | head::tail -> 
            yield! (g tail) |> Seq.collect (fun x -> [| for i in 0..head -> Array.append [|i|] x |]) 
    }
g [0;1;2] |> Seq.iter (fun x -> printfn "%A" x)

実行結果

[|0; 0; 0|]
[|0; 1; 0|]
[|0; 0; 1|]
[|0; 1; 1|]
[|0; 0; 2|]
[|0; 1; 2|]
val it : unit = ()

また、これをRでやるならexpand.grid関数一発で

> expand.grid(0:1,4:5,1:2)
  Var1 Var2 Var3
1    0    4    1
2    1    4    1
3    0    5    1
4    1    5    1
5    0    4    2
6    1    4    2
7    0    5    2
8    1    5    2

とできる。

昔、二次元でやってた。