在F#中查找列表最大值的索引

时间:2016-02-24 00:31:47

标签: f# max

我正在尝试编写一个以列表为例的函数

let list = [5;23;29;1] 
let x = max list // This will return 2 because 29 will be the max value and it's "indexed" at position 2

我不确定如何编写max函数

由于我的列表中只包含四个元素,因此我目前有一些像这样的代码

let list = (1, newMap1 |> getScore) :: (2, newMap2 |> getScore) :: (3, newMap3 |> getScore) :: (4, newMap4 |> getScore) :: []

我认为这是一种糟糕的方法,但在找到(x, _)的最大值后,我仍然坚持如何返回(_, y)。我对命令式方法非常有信心,但我对如何在功能上做这件事感到困惑

3 个答案:

答案 0 :(得分:6)

有几种方法可以做到这一点。在低级别,您可以编写递归函数来迭代和模式匹配列表。如果你正在学习F#,这是一个很好的练习。

同样,您可以使用fold函数实现此功能。在这里,我们的想法是保持一些状态,包括“最佳价值”和最佳价值指数。在每一步,我们要么保留原始信息,要么更新它:

let _, maxValue, maxIndex = 
  list |> List.fold (fun (index, maxSoFar, maxIndex) v -> 
    if v > maxSoFar then (index+1, v, index+1)
    else (index+1, maxSoFar, maxIndex)) (-1, System.Int32.MinValue, -1)

最后,我能想到的最短选项是使用mapimaxBy函数:

list
|> Seq.mapi (fun i v -> i, v)
|> Seq.maxBy snd

答案 1 :(得分:3)

这里只是使用模式匹配和递归的答案。

let list = [5;23;29;1] 

let rec findIndexOfMaxValue (maxValue:int) indexOfMaxValue currentIndex aList = 
    match aList with
    | [] -> indexOfMaxValue                                                            
    | head::tail -> match head with
                    | head when head > maxValue ->  findIndexOfMaxValue head currentIndex (currentIndex + 1) tail
                    | _ -> findIndexOfMaxValue maxValue indexOfMaxValue (currentIndex + 1) tail

[<EntryPoint>]
let main argv = 

    let indexOfMaxValue = findIndexOfMaxValue 0 0 0 list
    printfn "The index of the maximum value is %A." indexOfMaxValue
    //The index of the maximum value is 2.

    0

出于兴趣,我制作了一个计时脚本,将我的算法与其他提供的算法进行比较:

open System.Diagnostics

let n = 5000
let random = System.Random 543252

let randomlists = 
  [for i in [1..n] -> [ for i in [1..n] -> random.Next (0, n*n)]]


let stopWatch = 
  let sw = Stopwatch ()
  sw.Start ()
  sw

let timeIt (name : string) (a : int list -> 'T) : unit = 
  let t = stopWatch.ElapsedMilliseconds
  let v = a (randomlists.[0])
  for i = 1 to (n - 1) do
    a randomlists.[i] |> ignore
  let d = stopWatch.ElapsedMilliseconds - t
  printfn "%s, elapsed %d ms, result %A" name d v


let rec findIndexOfMaxValue (maxValue:int) indexOfMaxValue currentIndex aList = 
        match aList with
        | [] -> indexOfMaxValue                                                            
        | head::tail -> match head with
                        | head when head > maxValue ->  findIndexOfMaxValue head currentIndex (currentIndex + 1) tail
                        | _ -> findIndexOfMaxValue maxValue indexOfMaxValue (currentIndex + 1) tail

let findIndexOfMaxValueFoldAlg list =
    let _, maxValue, maxIndex  = 
       list |> List.fold (fun (index, maxSoFar, maxIndex) v -> 
        if v > maxSoFar then (index+1, v, index+1)
        else (index+1, maxSoFar, maxIndex)) (-1, System.Int32.MinValue, -1)
    maxIndex

let findIndexOfMaxValueSimpleSeq list = list
                                        |> Seq.mapi (fun i v -> i, v)
                                        |> Seq.maxBy snd
                                        |> fst
let findIndexOfMaxValueSimpleList list =
        list
        |> List.mapi (fun i x -> i, x)
        |> List.maxBy snd
        |> fst
[<EntryPoint>]
let main argv = 

    timeIt "recursiveOnly" (findIndexOfMaxValue 0 0 0)
    timeIt "simpleSeq" findIndexOfMaxValueSimpleSeq
    timeIt "simpleList" findIndexOfMaxValueSimpleList
    0

我得到的结果是:

recursiveOnly, elapsed 356ms, result 3562
foldAlgorithm, elapsed 1602ms, result 3562
simpleSeq, elapsed 4504ms, result 3562
simpleList, elapsed 4395ms, result 3562

答案 2 :(得分:2)

我在帮助库中有这些功能:

module List =
    let maxIndexBy projection list =
        list
        |> List.mapi (fun i x -> i, projection x)
        |> List.maxBy snd
        |> fst

    let maxIndex list = maxIndexBy id list

返回max元素的索引,可选择使用给定的投影函数。您可以通过替换&#34; List&#34;来轻松地为Seq和Array模块编写相同的函数。部分和重命名参数。