module FizzbuzzImperative =
let nums = [1..100]
let fizzbuzzImperative i =
if i % 3 = 0 && i % 5 = 0 then
printfn "fizzbuzz"
elif i % 3 = 0 then
printfn "fizz"
elif i % 5 = 0 then
printfn "buzz"
else printfn "%d" i
looks great!
//ok
module fizzbuzzPatternMatch =
let nums = [1..100]
let fizzbuzz i =
match i with
| i when i % 15 = 0 -> "fizzbuzz"
| i when i % 3 = 0 -> "fizz"
| i when i % 5 = 0 -> "buzz"
| _ -> string i
for i = 1 to 100 do
fizzbuzz i |> printfn "%s"
Great (u probably just forgot the list declaration on top).
But out of curiosity, sometimes you will see a function
keyword in code like this, it is just a syntax sugar for a function doing pattern matching on its parameter so:
let fizzbuzz i =
function
| i when i % 15 = 0 -> "fizzbuzz"
| i when i % 3 = 0 -> "fizz"
| i when i % 5 = 0 -> "buzz"
| i -> string i
is the same.
module FizzbuzzRecursive =
let nums = [1..100]
let fizzbuzz i =
match i with
| i when i % 15 = 0 -> "fizzbuzz"
| i when i % 3 = 0 -> "fizz"
| i when i % 5 = 0 -> "buzz"
| _ -> string i
let rec fizzbuzzRecursive (numList: int list) =
if not numList.IsEmpty then
printfn "%s" (fizzbuzz numList.Head)
fizzbuzzRecursive numList.Tail
Good, but usually list manipulation is done using pattern matching:
let rec fizzbuzzRecursive (numList: int list) =
match numList with
| h::t -> printfn "%s" (fizzbuzz h)
fizzbuzzRecursive t
| [] -> ignore()
the ::
is the cons operator that concatenates heads and tails. It is also usually a good practice to separate the side efects from the logic. (in large projects, this practice pushes dependencies to the edges of your program.):
let fizzBuzzRecursive = numList
match numList with
| h::t -> (fizzbuzz h)::(fizzBuzzRecursive t)
| [] -> []
let printStringList strList =
for s in strList do printfn "%s"
let program lst = fizzBuzzRecursive lst |> printStringList
or....
let rec fizzBuzzRecursive = function
| h::t -> (fizzbuzz h)::(fizzBuzzRecursive t)
| [] -> []
Succinct, but the above code is not tail-call optimized, we can go into that latter
module FizzbuzzSeq =
let nums = Seq.init 100 id
let fizzbuzz i =
match i with
| i when i % 15 = 0 -> "fizzbuzz"
| i when i % 3 = 0 -> "fizz"
| i when i % 5 = 0 -> "buzz"
| _ -> string i
let fizzbuzzResult = fizzbuzz |> Seq.map <| nums
Seq.iter (fun str -> printfn "%s" str) fizzbuzzResult
It works, but usually we pass the function to Seq.map
, so that it reads as Given this sequence, map its elements with this function
let fizzbuzzResult = nums |> Seq.map fizzbuzz
|> Seq.iter (printfn "%s")
As a rule of thumb, try to avoid <| operator (at first)
module FizzbuzzSumType =
type FizzBuzzAnswer =
| Fizz
| Buzz
| FizzBuzz
type Answer =
| FBZ of FizzBuzzAnswer
| Number of i: int
let fizzbuzz i : Answer =
match i with
| i when i % 15 = 0 -> FBZ(FizzBuzz)
| i when i % 3 = 0 -> FBZ(Fizz)
| i when i % 5 = 0 -> FBZ(Buzz)
| _ -> Number(i)
let toStr (x: Answer) : string =
match x with
| Number(i) -> string i
| FBZ(FizzBuzz) -> "FizzBuzz"
| FBZ(Fizz) -> "Fizz"
| FBZ(Buzz) -> "Buzz"
let printFizzbuzz (str: string) =
printfn "%s" str
let intToStr = fizzbuzz >> toStr
// let result = Seq.map intToStr [1..100]
Seq.iter (intToStr >> printFizzbuzz) [1..100]
I would personally use a single Sum type, to make it less noisy
type FizzBuzzAnswer =
| Fizz
| Buzz
| FizzBuzz
| Number of i
And as before, I would try to push the "print" to the edge of the execution
[1..100] |> Seq.map (fizzbuzz >> toStr) |> Seq.iter (printfn "%s")
which also make the information flow more explicit