我是F#的新手,我正在努力适应整个功能编程思维模式。基本上我试图弄清楚如何迭代数字列表并将它们加起来,而总和小于一定数量(例如200)。
目前,我有以下几点:
let nums = [20 .. 50]
let mutable total = 0
let addUp (num, (total : byref<int>)) =
if (num + total < 200) then
total <- total + num
for num in nums do
addUp (num, &total)
这确实完成了工作,但我觉得必须有一个更合适的方法来做到这一点,同时仍然保持在函数式编程的风格之内,而不必依赖于一个可变的。我仍然觉得我正在接近强制性思维方式。
我以为我可以以某种方式使用List.fold来做这个但是经过一些实验后我无法弄清楚如何将它与整体一起使用&lt; 200件事。任何帮助都会非常感激,如果这是一个常见问题和回答的问题,我会提前道歉(我在自己的搜索中找不到任何东西)。
答案 0 :(得分:1)
首先,让我们尝试避免使用可变变量,其中一种方法是创建一个递归函数来迭代列表中的最新总数。
let rec addUp total nums =
match nums with
| [] -> total
| num::tl ->
let newTotal = num + total
if newTotal < 200 then
addUp newTotal tl
else
total
addUp 0 nums
我们创建一个递归函数addUp
,它接受总数和数字列表。如果新总数少于200,该函数将提取列表的第一个数字并将其添加到总数中。由于列表中可能仍有更多数字,我们再次使用新总数调用addUp
,其余部分数字。否则,我们停止并返回新的总数。
我们也可以使用List.fold
来使代码更清晰,但整个列表将被迭代。这是代码:
List.fold (fun total num ->
let newTotal = num + total
if newTotal < 200 then
newTotal
else
total
) 0 nums
由于输出与输入的成员类型相同,我们可以使用List.reduce
并删除初始状态(0)。
List.reduce (fun total num ->
let newTotal = num + total
if newTotal < 200 then
newTotal
else
total
) nums
希望这有帮助。
答案 1 :(得分:1)
出于教育目的,上面的答案很好,因为它可以让您了解fold
或reduce
中究竟发生了什么。
对于真实项目,最好使用现有的库函数。您甚至可以将求和和绑定检查分开。考虑一下:
let addUp threshold xs =
xs
|> Seq.scan (+) 0 // line 1
|> Seq.takeWhile (fun x -> x < threshold) // line 2
|> Seq.last // line 3
// Usage:
let nums = Seq.initInfinite ( (+) 20) // line 4
nums
|> addUp 200
|> printfn "%d" // prints "188"
一点解释:
Seq.scan (fun state x -> state + x) 0
的缩写,因此它实际返回一系列中间和(20, 41, 63, ...
); Seq.initInfinite (fun x -> 20+x)
的收缩。我冒昧地将您的数据设为无限序列(20, 21, 22, ...
),但它仍然有效。注意,代码看起来像三个调用,但序列只评估一次。
注意,在第2行,不要像上面那样尝试收缩。原因是(<) threshold
评估为fun x -> threshold < x
这是错误的。如果你。但是,使用(>) threshold
,它反直觉并且令人困惑。
注意,函数中没有错误检查。使用空源序列调用它将在Seq.last
处崩溃。