2013-10-16 12 views
5

Jak ogólnie można połączyć dwie tablice 2d?Konkluzja macierzy 2D w f #

Moje założenie (Mógłbym oczywiście łatwo to sprawdzić) jest to, że zawsze mają taką samą liczbę kolumn:

let concatArrays (arr1:obj[,]) (arr2:obj[,]) = 
    ([arr1; arr2]) |> Array2d.concat 

Ta funkcja robi istnieje chociaż. Aby było jasne, wynik powinien dać tablicę 2d o długości = suma długości i tej samej liczbie kolumn co oryginalne tablice2D i powinna być tego samego typu co wejście, tutaj obj [,]. Oczywiście mogłem to zrobić w konstrukcji pętli, ale zastanawiałem się nad f # way. Dzięki.

Próbowałem to:

let append2D (arr1:float[,]) (arr2:float[,]) = 
    let cls = arr1.GetLength 1 
    let rows1 = arr1.GetLength 0 
    let rows2 = arr2.GetLength 0 
    Array2D.init (rows1+rows2) cls (fun i j -> match i with | a when a <= rows1 -> arr1.[i,j] | _ -> arr2.[i,j]) 

Ale ta wraca z indeksu poza błędem bounds.

Aktualizacja ostatnim rzędzie:

Array2D.init (rows1+rows2) cls (fun i j -> if i < rows1 then arr1.[i,j] else arr2.[i,j]) 

Aktualizacja roztwór roboczy:

Array2D.init (rows1+rows2) cls (fun i j -> if i < rows1 then arr1.[i,j] else arr2.[i-rows1,j]) 

Dzięki wszystkim

+1

[This] (http://stackoverflow.com/a/2367927/969613) może pomóc – JMK

+0

Dzięki.Ale czy to nie łączy wielu macierzy 1d w jedną tablicę 2D? co chcę zrobić, to połączyć wiele tablic 2D w jedną tablicę 2d. Ale może dobry punkt wyjścia. Czy nie dostaję tego? – nik

+1

Jeśli dobrze cię rozumiem, możesz po prostu użyć polecenia 'Array.append'. Jeśli się mylę, proszę podać przykładowy sygnał wejściowy i wyjściowy. – Tobber

Odpowiedz

6

Po tym recommendation Oto concat funkcję przez dwie równej wielkości kolumna Array2D argumentów dowolnego typu 'a:

let concat (a1: 'a[,]) (a2: 'a[,]) = 
    let a1l1,a1l2,a2l1,a2l2 = (Array2D.length1 a1),(Array2D.length2 a1),(Array2D.length1 a2),(Array2D.length2 a2) 
    if a1l2 <> a2l2 then failwith "arrays have different column sizes" 
    let result = Array2D.zeroCreate (a1l1 + a2l1) a1l2 
    Array2D.blit a1 0 0 result 0 0 a1l1 a1l2 
    Array2D.blit a2 0 0 result a1l1 0 a2l1 a2l2 
    result 

Można to sprawdzić doświadczalnie, ale byłoby to razy lepsze osiągi, niż jakiegokolwiek wariantu opartego na Array2D.init ponieważ Array2D.zeroCreate i Array2D.blit implementacje są wysoce zoptymalizowane.

1

@Gene dostarczyło doskonałe rozwiązanie, przy użyciu wbudowanej funkcji blit wydaje się być bardzo przydatne tutaj.

chciałbym pisać moją wykorzystanie jego funkcji jako rozszerzenie do tablicy modułów i Array2D dla tych, którzy mogą się przydać:

module Array = 
    let first (arr: 'a array) = arr.[0] 
    let others (arr: 'a array) = arr.[1..] 
    let split arr = first arr, others arr 

module Array2D = 
    let joinByRows (a1: 'a[,]) (a2: 'a[,]) = 
     let a1l1,a1l2,a2l1,a2l2 = (Array2D.length1 a1),(Array2D.length2 a1),(Array2D.length1 a2),(Array2D.length2 a2) 
     if a1l2 <> a2l2 then failwith "arrays have different column sizes" 
     let result = Array2D.zeroCreate (a1l1 + a2l1) a1l2 
     Array2D.blit a1 0 0 result 0 0 a1l1 a1l2 
     Array2D.blit a2 0 0 result a1l1 0 a2l1 a2l2 
     result 

    let joinByCols (a1: 'a[,]) (a2: 'a[,]) = 
     let a1l1,a1l2,a2l1,a2l2 = (Array2D.length1 a1),(Array2D.length2 a1),(Array2D.length1 a2),(Array2D.length2 a2) 
     if a1l1 <> a2l1 then failwith "arrays have different row sizes" 
     let result = Array2D.zeroCreate a1l1 (a1l2 + a2l2) 
     Array2D.blit a1 0 0 result 0 0 a1l1 a1l2 
     Array2D.blit a2 0 0 result 0 a1l2 a2l1 a2l2 
     result 

    // here joiner function must be Array2D.joinByRows or Array2D.joinByCols 
    let joinMany joiner (a: seq<'a[,]>) = 
     let arrays = a |> Array.ofSeq 
     if Array.length arrays = 0 then 
      failwith "no arrays" 
     elif Array.length arrays = 1 then 
      Array.first arrays 
     else 
      let rec doJoin acc arrays = 
       if Array.length arrays = 0 then 
        acc 
       elif Array.length arrays = 1 then 
        joiner acc (Array.first arrays) 
       else 
        let acc = joiner acc (Array.first arrays) 
        doJoin acc (Array.others arrays) 
      doJoin <|| Array.split arrays 
      // or doJoin arrays.[0] arrays.[1..] 
0

@Rustam, dzięki za to. Potrzebowałem tych funkcji do pracy z zakresami Excela. Po użyciu tych funkcji znalazłem kilka miejsc do poprawy.

Najpierw, zamiast zakładać, że a1 i a2 mają indeksy zerowe, polecam użycie Array2D.base1 i Array2D.base2 dla indeksów w funkcjach Array2D.blit. Zauważyłem, że zajęło mi to około 4 godzin wyciągania włosów, aby dowiedzieć się, że to właśnie powoduje pewne problemy w moim kodzie.

czyli dla funkcji joinByRows:

Array2D.blit a1 (Array2D.base1 a1) (Array2D.base2 a1) result 0 0 a1l1 a1l2 
Array2D.blit a2 (Array2D.base1 a2) (Array2D.base2 a2) result a1l1 0 a2l1 a2l2 

drugie, czynność joinMany można znacznie uprościć stosując Seq.fold:

let joinMany joiner (a: seq<'a[,]>) = 
     Seq.fold joiner (Seq.head a) (Seq.tail a) 

nie zrobił sprawdzić wydajność, ale to sobie wyobrazić, że wbudowana w funkcji byłoby bardziej zoptymalizowane.