Search code examples
collectionsf#take

Take while running total smaller than value


I am trying to generate a list of even integers while the sum of the items in the list is less equal a given number. For instance if the threshold k is 20, then the expected output is [0;2;4;6;8]

I can generate a list where the largest value is smaller by the threshold like this:

let listOfEvenNumbersSmallerThanTwenty =
    Seq.unfold (fun x -> Some(x, x + 1)) 0 // natural numbers
    |> Seq.filter (fun x -> x % 2 = 0) // even numbers
    |> Seq.takeWhile (fun x -> x <= 20)
    |> List.ofSeq

(I know that I can combine the unfold and filter to Some(x, x + 2) but this task is for educational purposes)

I managed to create a different list with a running total smaller than the threshold:

let runningTotal =
    listOfEvenNumbersSmallerThanTwenty 
    |> Seq.scan (+) 0
    |> Seq.filter (fun x -> x < 20)
    |> List.ofSeq

But in order to do that, I have set the threshold in listOfEvenNumbersSmallerThanTwenty (which is way more than the items needed) and I have lost the initial sequence. I did also try to find that using a mutable value but didn't really like that route.


Solution

  • You can create a small predicate function that will encapsulate a mutable sum.

    let sumLessThan threshold =
        let mutable sum = 0
        fun x ->
            sum <- sum + x
            sum < threshold
    

    Usage is very simple and it can be applied to any sequence

    Seq.initInfinite ((*) 2) |> Seq.takeWhile (sumLessThan 20)
    

    There is nothing bad in using mutable state when its encapsulated (check usages of the mutable variable in Seq module)