5

我一般如何组合两个二维数组?

我的假设(我显然可以很容易地对此进行测试)是它们总是具有相同数量的列:

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

虽然这个功能不存在。为了清楚起见,结果应该生成一个 2d 数组,其长度 = 长度之和,列数与原始 arrays2D 相同,并且应该与输入类型相同,此处为 obj[,]。我显然可以在循环结构中做到这一点,但我想知道一种 f# 方式。谢谢。

我试过这个:

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

但这会带来索引越界错误。

最后一行的更新:

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

更新工作解决方案:

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

谢谢大家

4

3 回答 3

7

遵循此建议,这里有一个用于任何类型concat的两个相等列大小参数的函数:Array2D'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

您可以通过实验检查这一点,但它的性能将比任何基于Array2D.init因为Array2D.zeroCreateArray2D.blit实现高度优化的变体好几倍。

于 2013-10-16T14:39:14.890 回答
1

@Gene 提供了一个很好的解决方案,在这里使用内置的 blit 函数似乎非常有用。

我想将我对他的函数的使用作为模块 Array 和 Array2D 的扩展发布给那些可能觉得它有用的人:

 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..] 
于 2014-01-02T12:43:49.643 回答
0

@Rustam,谢谢你。我需要这些功能来处理 excel 范围。使用这些功能后,我发现了一些需要改进的地方。

首先,我建议不要假设 a1 和 a2 具有从零开始的索引,而是使用 Array2D.base1 和 Array2D.base2 作为 Array2D.blit 函数中的索引。请注意,我花了大约 4 个小时才弄清楚这是导致我的代码出现问题的原因。

即对于 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

其次,使用 Seq.fold 可以显着简化您的 joinMany 函数:

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

我没有检查性能,但我想内置功能会更加优化。

于 2017-07-01T04:17:12.090 回答