2013-10-16 16 views
5

Come è possibile combinare due array 2d in generale?Array concat 2D in f #

mia ipotesi (ho potuto ovviamente facilmente verificare questo) è che hanno sempre lo stesso numero di colonne:

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

Questa funzione doesnt esiste però. Per essere chiari, il risultato dovrebbe produrre un array 2d con lunghezza = somma delle lunghezze e lo stesso numero di colonne delle matrici originali2D e dovrebbe essere dello stesso tipo dell'input, qui obj [,]. Ovviamente potrei farlo in un costrutto ciclico, ma mi stavo chiedendo un modo f #. Grazie.

ho provato questo:

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]) 

Ma questo torna con indice di errore di limiti.

Aggiornamento dell'ultima riga:

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

Aggiornamento soluzione di lavoro:

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

grazie tutti

+1

[Questo] (http://stackoverflow.com/a/2367927/969613) può aiutare – JMK

+0

Grazie.Ma questo non unisce più array 1d in un array 2d? quello che voglio fare è unire più array 2d in un array 2d. Ma forse un buon punto di partenza. O non lo capisco? – nik

+1

Se ho capito bene, puoi semplicemente usare 'Array.append'. Se ho torto, ti preghiamo di fornire un esempio di input e output. – Tobber

risposta

6

Seguendo questo recommendation ecco una funzione concat per due uguali dimensioni della colonna Array2D argomenti di qualsiasi tipo '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 

Potete verificare questo sperimentalmente, ma sarebbe tempo prestazioni migliori, rispetto a qualsiasi variante basata su Array2D.init perché Array2D.zeroCreate e Array2D.blit implementazioni sono altamente ottimizzato.

1

@Gene ha fornito una soluzione eccellente, utilizzando la funzione di blit incorporata sembra essere molto utile qui.

vorrei inviare il mio utilizzo della sua funzione come estensione ai moduli Array e Array2D per coloro che potrebbero trovare utile:

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, grazie per questo. Avevo bisogno di queste funzioni per lavorare con gli intervalli Excel. Dopo aver usato queste funzioni ho trovato alcuni punti di miglioramento.

Prima di assumere che a1 e a2 abbiano indici a base zero, si consiglia di utilizzare Array2D.base1 e Array2D.base2 per gli indici nelle funzioni Array2D.blit. Nota, mi ci sono volute circa 4 ore per tirare fuori i miei capelli per capire che questo è ciò che stava causando alcuni problemi nel mio codice.

cioè per la funzione 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 

In secondo luogo, la funzione joinMany può essere semplificata in modo significativo utilizzando Seq.fold:

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

Ho fatto controllare le prestazioni ma immagino che la costruzione in funzione sarebbe più ottimizzato.