F#二进制搜索返回位置

F#二进制搜索返回位置,f#,functional-programming,binary-search,F#,Functional Programming,Binary Search,我试图在F#中实现一个简单的二进制搜索,作为学习函数式编程的一种方式。我已经在我的主要语言C#中以命令式的方式实现了它,但我认为通过在F#中以递归的方式实现它,这将是一个很好的挑战,也是学习函数概念的好方法。我已经做得够多了,它可以正确地找到值,我的问题是,如果值不在数组中,我希望函数返回-1,否则返回值的索引,但是我找不到跟踪索引的好方法。这是我的密码: let rec chop i (nums:array<int>) = match nums with | [||] ->

我试图在F#中实现一个简单的二进制搜索,作为学习函数式编程的一种方式。我已经在我的主要语言C#中以命令式的方式实现了它,但我认为通过在F#中以递归的方式实现它,这将是一个很好的挑战,也是学习函数概念的好方法。我已经做得够多了,它可以正确地找到值,我的问题是,如果值不在数组中,我希望函数返回-1,否则返回值的索引,但是我找不到跟踪索引的好方法。这是我的密码:

let rec chop i (nums:array<int>) =
match nums with
| [||] -> -1
| [|x|] -> if x = i then x else -1
| _ -> 
    let mid = nums.Length / 2 in
    if nums.[mid] < i then (chop i nums.[mid..])
    elif nums.[mid] > i then (chop i nums.[..mid])
    else mid

[<EntryPoint>]
let main argv = 
    printfn "%d" (chop 20 (Array.init 100 (fun index -> index * 2)))
    System.Console.ReadKey() |> ignore
    0 // return an integer exit code
let rec chop i(nums:array)=
将nums与
| [||] -> -1
|[| x |]->如果x=i,那么x else-1
| _ -> 
让中间=单位长度/2英寸
如果是nums.[mid]i然后(印章i nums.[..mid])
埃尔斯米德
[]
让主argv=
printfn“%d”(第20章(Array.init 100(趣味索引->索引*2)))
System.Console.ReadKey()|>忽略
0//返回整数退出代码
因为每次迭代我都会分割列表,所以当我向上移动到数组中时,我会得到不同的索引。在我的示例中,代码返回3而不是10


建议用什么方法解决这个问题?任何解决这个问题的方法都是值得赞赏的,尽管我显然更喜欢用“正确”的方法从功能的角度来做,因为这就是我正在练习的。我还希望得到关于我的解决方案从性能角度看是否不好的建议(例如,首先拆分阵列是一个坏主意吗?

我会更多地使用原始阵列,而不复制阵列(如果失败,请使用
选项
而不是幻数
-1
):

更具可读性(但有点陈词滥调):


这当然在数学上是相同的,但对于有限的范围和溢出相当大的差异

一个微小的变化,这不需要“不可能”的情况:

让二进制搜索值(数组:'T[])=
让rec循环lo hi=
如果lo>>1)
将数组。[mid]与
|当x=值->某个中间值时的x
|x循环(中间+1)高时的x
|_u->环路lo(mid-1)
没有别的
循环0(array.Length-1)

回答得很好,但请注意,对于非常大的数组,
(a+b)/2
可能在加法过程中溢出,并导致负索引。看。@kvb-是的,对-你认为我应该在这里更改它吗?(这个值比正确的实现更具可读性,IMO),IMO,不需要一个“不可能”的案例。你为什么不把它作为一个答案@Daniel(我不能整天偷别人的好主意:D)-但你是对的,我应该摆脱它(现在我想起来它真的很容易;))顺便说一句:如果传递偏移索引(当前数组相对于原始数组的起始位置),仍然可以修复go
let chop i (nums : int[]) =
    let rec find a b =
        if b < a then None else
        let mid = (a+b)/2
        let midValue = nums.[mid]
        if midValue = i then 
            Some mid 
        elif i < midValue then
            find a (mid-1)
        else
            find (mid+1) b
    find 0 (nums.Length-1)
let chop i (nums : int[]) =
    let rec find a b =
        if b < a then None else
        let mid = (a+b)/2
        match (compare i nums.[mid]) with
        | 0  -> Some mid
        | -1 -> find a (mid-1)
        | 1  -> find (mid+1) b
        | _  -> failwith "should never happen"
    find 0 (nums.Length-1)
[<Literal>] 
let Lt = -1
[<Literal>] 
let Gt = +1


let chop i (nums : int[]) =
    let rec find a b =
        if b < a then None else
        let mid = (a+b)/2
        match (compare i nums.[mid]) with
        | Lt -> find a (mid-1)
        | Gt -> find (mid+1) b
        | Eq -> Some mid
    find 0 (nums.Length-1)
let mid = a + (b - a) / 2
let binarySearch value (array: 'T[]) =
    let rec loop lo hi =
        if lo <= hi then
            let mid = lo + ((hi - lo) >>> 1)
            match array.[mid] with
            | x when x = value -> Some mid
            | x when x < value -> loop (mid + 1) hi
            | _ -> loop lo (mid - 1)
        else None
    loop 0 (array.Length - 1)