在F#中连接二维数组

5

如何一般性地合并两个二维数组?

我假设(显然可以轻松测试)它们总是具有相同的列数:

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

这个函数实际上不存在。需要明确的是,结果应该产生一个二维数组,长度等于长度总和,列数与原始2D数组相同,并且应该与输入类型相同,即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])  

感谢大家。

1
这个可能会有帮助:https://dev59.com/WUzSa4cB1Zd3GeqPllEl#2367927 - JMK
谢谢。但是这个方法不是将多个一维数组合并成一个二维数组吗?我想要的是将多个二维数组合并成一个二维数组。但这可能是一个好的起点。或者是我理解有误? - nik
1
如果我理解你的意思正确的话,你可以直接使用Array.append。如果我理解错误,请提供一个输入和输出的示例。 - Tobber
1
在你的条件语句中使用 < 代替 <=,或者更简洁地写成: if i < rows1 then arr1.[i,j] else arr2.[i,j] - Tobber
1
是的:arr2.[i,j] 应该改为 arr2.[i-rows1,j] :) - Tobber
显示剩余4条评论
3个回答

7

根据这里的建议,以下是一个针对任何类型'a的两个等大小列的Array2D参数的concat函数:

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 实现方式都经过高度优化。


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

0

@Rustam,谢谢你。我需要这些函数来处理Excel范围。在使用这些函数后,我发现有一些可以改进的地方。

首先,建议在Array2D.blit函数中使用Array2D.base1和Array2D.base2作为索引,而不是假定a1和a2具有从零开始的索引。请注意,我花了大约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

其次,您的joinMany函数可以通过使用Seq.fold大大简化:

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

我没有检查过性能,但我想内置函数应该更加优化。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接