取 while 运行 total 小于 value

Take while running total smaller than value

我正在尝试生成一个偶数列表,而列表中各项的总和小于给定的数字。 例如,如果阈值 k 为 20,则预期输出为 [0;2;4;6;8]

我可以生成一个列表,其中最大值小于阈值,如下所示:

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

(我知道我可以将展开和过滤结合到 Some(x, x + 2) 但此任务仅用于教育目的)

我设法创建了一个 运行 总数小于阈值的不同列表:

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

但为了做到这一点,我在 listOfEvenNumbersSmallerThanTwenty 中设置了阈值(这远远超过了所需的项目)并且我丢失了初始序列。我也确实尝试过使用可变值找到它,但我并不真的喜欢那条路线。

这是一个我认为非常优雅的解决方案(虽然不是最有效的):

let evens = Seq.initInfinite (fun i -> 2 * i)
Seq.initInfinite (fun i -> Seq.take i evens)
    |> Seq.takeWhile (fun seq ->
        Seq.sum seq <= 20)
    |> Seq.last
    |> List.ofSeq

您可以创建一个小型谓词函数来封装可变总和。

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

用法非常简单,可以应用于任何序列

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

封装时使用可变状态没有什么不好(检查Seq模块中可变变量的用法)