(F#)查找2D数组中元素的索引

时间:2018-04-17 23:28:47

标签: arrays multidimensional-array f#

我目前在查找2D数组中特定元素的索引时遇到了一些问题。我知道标准数组可以使用findIndex,如下所示。

let wantedElemented = 5
let index = Array.findIndex(fun x -> x = wantedElemented) array

我的问题是,你如何将它应用于2D数组(矩形)。有没有比比较每个元素迭代整个数组更有效的方法?

for row in 0 .. max do
        for col in 0 .. max do
            if array.[row,col] = wantedElement then 
            let index = (row,col)
            index
            else (-1,-1)

如果我必须遍历整个数组,如何在不使用选项类型的情况下处理else条件

4 个答案:

答案 0 :(得分:4)

回应你的评论:是的,数组不能与头尾类似的列表匹配。但他们确实有指标! : - )

递归函数不必递归数据结构。递归路径可以由不同的东西定义。例如:为什么我们不能从零到最大(或后退)的数组指标进行递归?

let find2D needle (arr: int [,]) = 
    let rec go x y =
          if   y >= arr.GetLength 1 then None
          elif x >= arr.GetLength 0 then go 0 (y+1)
          elif arr.[x,y] = needle   then Some (x,y)
          else go (x+1) y
    go 0 0

上述解决方案将按行扫描数组,直到找到needle,此时它将立即返回。

或者,您可以自己生成可选指标的序列,然后使用Seq.tryPick选择该序列中不是None的第一个元素:

let find2D needle (arr: int [,]) = Seq.tryPick id <| seq {
    for i in 0..(arr.GetLength 0 - 1) do
        for j in 0..(arr.GetLength 1 - 1) do
            if arr.[i,j] = needle 
                then yield Some (i,j) 
                else yield None
}

由于序列的工作原理(它们是懒惰的),这只会迭代直到找到第一个Some,然后停止。这稍微简单一点(就可读性而言),但也比上面的普通递归解决方案稍微低一些,因为这里我们承担了创建和维护序列的开销。

答案 1 :(得分:2)

Fyodor Soikin指的是像......

module Array2D =
    let findIndex f (array:'a[,]) =
        let xStart = array.GetLowerBound 0
        let xEnd   = array.GetUpperBound 0
        let yStart = array.GetLowerBound 1
        let yEnd   = array.GetUpperBound 1
        let rec iterate i j =
            if   f array.[i,j] then Some (i, j)
            elif j < yEnd      then iterate i (j+1)
            elif i < xEnd      then iterate (i+1) yStart
                               else None
        iterate xStart yStart

[<EntryPoint>]
let main argv =
    let testArray = Array2D.init 20 20 (fun _ _ -> 0)
    testArray.[13,12] <- 1

    match testArray |> Array2D.findIndex (fun x -> x = 1) with
    | Some (x,y) -> printfn "found at (%d,%d)" x y
    | None -> printfn "not found"

    0

此外,使用谓词函数执行此操作而不是查找特定值的原因是由于f#执行相等性检查的方式(否则如果您要替换元素比较,那么我会建议函数是标有“内联”)

答案 2 :(得分:1)

另一个想法是让一个函数懒惰地枚举2d数组的各个行,然后尝试找到那些行的元素:

let rows arr2D =
    seq {
        for i in Array2D.base1 arr2D .. Array2D.length1 arr2D - 1 -> arr2D.[i, *]
    }

let find2D arr2D elem =
    arr2D
    |> rows 
    |> Seq.mapi (fun i arr ->
        Array.tryFindIndex ((=) elem) arr 
        |> Option.map (fun j -> i, j)) 
    |> Seq.pick id

或者,如果元素可以在多个地方找到,并且您想要所有元素的列表:

let findAll2D arr2D elem =
    arr2D
    |> rows 
    |> Seq.mapi (fun i arr ->
        Array.tryFindIndex ((=) elem) arr 
        |> Option.map (fun j -> i, j)) 
    |> Seq.choose id

答案 3 :(得分:0)

使用标准F#库,您可以实现这样的通用索引查找Array2D的功能,如下所示:

let findIndexes f (aa: 'a[,]) =
    let mutable found = None
    aa |> Array2D.iteri (fun x y a -> if found.IsNone && (f a) then found <- Some(x,y))
    found

并将其用作

findIndexes ((=)soughtValue) yourArray

此实现显然使用Array2D.iteri扫描整个数组,但在第一次匹配后的比较可能会通过上面的比较表达式中的短路进行略微优化。

最后,我坚持通过惯​​用Option<int,int>返回搜索结果。如果出于某种原因你想要在不使用该选项的情况下返回搜索结果,那么int*int元组就足以使用一些&#34;不可能的&#34;一对索引,如(-1,-1)作为初始found值和搜索失败的指示符,或者像Array.findIndex那样在搜索失败时抛出异常。