Reputation: 165
I am really new to F# and am struggling to adapt to the whole functional programming mindset. Basically I am trying to figure out how to iterate through a list of numbers and add them up while the sum is less than a certain number (200 for instance).
At the moment I have something along the lines of the following:
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)
This does get the job done, but I feel like there must be a more appropriate way to do this while still remaining within the style of functional programming, and without out having to rely on a mutable. I still feel like I'm approaching things from an imperative mindset.
I was thinking I could somehow use List.fold to do this but after some experimentation I was unable to figure out how to utilize it in conjunction with the whole while < 200 thing. Any help would be hugely appreciated and I apologize in advance if this is a commonly asked and answered question (I couldn't find anything through my own search).
Upvotes: 1
Views: 107
Reputation: 7735
For educational purposes, the answer above is fine as it lets you see what exactly happens within the fold
or reduce
.
For a real project, it is better to use existing library functions. You can even separate summation and bound checking. Consider this:
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"
A bit of explanation:
Seq.scan (fun state x -> state + x) 0
so it actually returns a sequence of intermediate sums (20, 41, 63, ...
);Seq.initInfinite (fun x -> 20+x)
. I took my liberty to make your data an infinite sequence (20, 21, 22, ...
), but it still works.Note, the code looks like three calls, but the sequence is evaluated only once.
Note, in line 2, don't try contraction like above. The reason is that (<) threshold
evaluates to fun x -> threshold < x
which is wrong. If you. however, use (>) threshold
, it reads counter-intuitive and confusing.
Note, there's no error checking in the function. Calling it with an empty source sequence will crash at Seq.last
.
Upvotes: 1
Reputation: 2231
First, let's try to avoid mutable variable and one way to do it is to create a recursive function to iterate through the list with the latest total.
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
We create a recursive function addUp
which accepts total and the list of numbers. The function extracts the first number of the list and adds it to the total if the new total is less than 200. Since we might still have more numbers on the list, we call addUp
again with the new total and the rest of the numbers. Otherwise, we stop and return the new total.
We can also use List.fold
which makes the code cleaner, but the whole list will be iterated. Here is the code:
List.fold (fun total num ->
let newTotal = num + total
if newTotal < 200 then
newTotal
else
total
) 0 nums
Since the output is the same type as the member of the input, we can use List.reduce
and get rid of initial state (0).
List.reduce (fun total num ->
let newTotal = num + total
if newTotal < 200 then
newTotal
else
total
) nums
Hope this helps.
Upvotes: 1