多次元配列を操作する際、深いネスト作ってループすんのやめたい
高次元の配列に対する処理を書こうと思うと、その各次元に対応したループインデックスに対する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
とできる。
昔、二次元でやってた。