Finding an index of a max value of a list in F#

时光总嘲笑我的痴心妄想 提交于 2019-12-10 18:25:28

问题


I'm trying to write a function that takes a list for example

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

I'm not sure about how to go about writing the max function

Since my list will only contain four elements I currently have some code like this

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

I consider this a terrible approach but I'm still stuck on how to return (x, _) after I find the max of (_, y). I'm very confident with imperative approaches but I'm stumped on how to do this functionally


回答1:


There is a couple of ways to do this. At the low-level, you can write a recursive function to iterate and pattern match over a list. This is good exercise if you are learning F#.

Similarly, you can implement this using the fold function. Here, the idea is that we keep some state, consisting of the "best value" and the index of the best value. At each step, we either keep the original information, or update it:

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)

Finally, the shortest option I can think of is to use mapi and maxBy functions:

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



回答2:


Here's an answer only using pattern matching and recursion.

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

Out of interest, I made a timing script comparing my algorithm with the other ones provided:

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

The results I get are:

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



回答3:


I have these functions in my helper library:

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

Returns the index of the max element, optionally using a given projection function. You can write the same functions for the Seq and Array modules easily by replacing the "List" part and renaming the arguments.



来源:https://stackoverflow.com/questions/35591062/finding-an-index-of-a-max-value-of-a-list-in-f

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!