Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to find the value in a list at which a maximum value of a function occurs

Tags:

f#

I want to find not just the maximum value of a function applied to a list (for which I would just use List.maxBy) but also the value in the list this occurred at. This feels like a fairly common operation and given the richness of the F# libraries in general I wouldn't be at all surprised to discover it was actually already available but I cannot seem to find it if it is!

To illustrate with an example, I want to be able to map a list domain and a function f

let domain = [0 .. 5]
let f x = -x * (x - 2)

to (1, 1) (since the function applied to an other element of the list is less than 1).

I first tried this:

let findMaximum domain f =
    let candidates = [ for x in domain do
                        yield x, f x ]
    let rec findMaximumHelper domain f currentMax =
        match domain with
        | [] -> currentMax
        | head::tail -> 
            let cand = f head
            match currentMax with
            | None ->
                let newMax = Some(head, cand)
                findMaximumHelper tail f newMax
            | Some(maxAt, possMax) ->
                let newMax =
                    if cand > possMax then Some(head, cand)
                    else Some(maxAt, possMax)
                findMaximumHelper tail f newMax
    findMaximumHelper domain f None

let answer = findMaximum domain f

at which point I realised this is very close to a fold operation, and put together

let findMaximum2 domain f =
    let findMaximumHelper f acc x =
        let cand = f x
        match acc with
        | None -> Some(x, cand)
        | Some(maxAt, possMax) ->
            if cand > possMax then Some(x, cand)
            else Some(maxAt, possMax)
    List.fold (findMaximumHelper f) None domain

let answer2 = findMaximum2 domain f

instead.

My question is, are these idiomatic F# ways of solving this problem, or indeed, is there a better way of solving this?

like image 708
Mike Lynch Avatar asked Dec 03 '22 08:12

Mike Lynch


2 Answers

Indeed, the F# library provides all the necessary higher order functions to express this succinctly:

domain
|> Seq.map (fun x -> x, f x)
|> Seq.maxBy snd

Note: updated to use Seq.map and Seq.maxBy instead of List.map and List.maxBy to address @ildjarn's concern about creating an unnecessary intermediate list.

like image 162
Stephen Swensen Avatar answered Jan 21 '23 01:01

Stephen Swensen


An alternative to Stephen's answer, that avoids creating a second List, with the tradeoff of executing f one extra time:

domain
|> List.maxBy f
|> fun x -> x, f x
like image 43
ildjarn Avatar answered Jan 21 '23 02:01

ildjarn