Yet Another Fizz Buzz (YAFB) in F#

Fizz Buzz kennt sicher jeder der auch Hello World kennt.
Meinst wird Fizz Buzz mit der Modulo Funktion gelöst.

Hier wird eine Lösung in F# gezeigt, die endlose Sequenzen verwendet und das Problem sehr beschreibend (deklarativ) löst, ohne dass man die mathematische Bedeutung der Modulo Funktion kennen muss.

Es werden zwei einfache Hilfsfunktionen verwendet die man eigentlich wiederverwendbar in einer Funktions-Bibliothek hinterlegen kann.
Mit der Funktion cycle werden unendlich lange Sequenzen der übergebenen Muster erzeugt.

   let cycle x = Seq.initInfinite (fun _ -> x) |> Seq.concat

Und zipWith verknüpft die Elemente zweier endlosen Sequenzen mittels der übergebenen Funktion f.

   let zipWith f xs ys = Seq.zip xs ys |> Seq.map (fun (x,y) -> f x y)

Nun zum eigentlichen Fizz Buzz, das sehr beschreibend gelöst ist, wie wenn man das Problem jemandem zum ersten mal erklärt.

Die Nummerierung von 1 bis 100 als String.

   let numbers  = seq [1 .. 100] |> Seq.map string

Zwei-mal nichts (“”) dann “fizz” und wiederholend (cycle).
Vier-mal nichts (“”) dann “buzz “und wiederholend (cycle).

   let fizz = seq [“”;””;”fizz”]       |> cycle
   let buzz = seq [“”;””;””;””;”buzz”] |> cycle

Dann die Logik wie alles zusammen agiert:

   let fizzBuzz = zipWith (+) fizz buzz |> zipWith max numbers

Mit zipWith (+) werden die zwei endlosen Sequenzen fizz und buzz elementweise zusammengefügt.
Aus zwei Leer-Strings ergibt sich so wieder ein Leer-String und “fizzbuzz” kommt so zustande “fizz” + “buzz”.

Etwas trickreich oder ungewohnt ist das zipWith max numbers, die Maximum Funktion max ist generisch und in diesem Anwendungsfall wird das Maximum von strings bestimmt. Man kennt das vom Sortieren von strings, diese werden anhand des ASCII codes sortiert.
So gibt auch max den String aus der weiter hinten im Alphabet respektive der ASCII Tabelle befindet.
Und da der Leer-String “” kleiner ist als “1”, “2” und “1” und “2” kleiner sind als “fizz” und “buzz” werden alle “” mit Inhalt also den numbers bestückt und “fizz” und “buzz” sind den Zahlen überlegen.

Und zu guter Letzt erfolgt die Ausgabe des Ergebnis der ersten 20 Elemente

   fizzBuzz |> Seq.take 20 |> Seq.toList

was folgendes anzeigt:

val it : string list =
  [“1”; “2”; “fizz”; “4”; “buzz”; “fizz”; “7”; “8”; “fizz”; “buzz”; “11”;
   “fizz”; “13”; “14”; “fizzbuzz”; “16”; “17”; “fizz”; “19”; “buzz”]

Und der vollständige F# Code:

 
let cycle x         = Seq.initInfinite (fun _ -> x) |> Seq.concat
let zipWith f xs ys = Seq.zip xs ys |> Seq.map (fun (x,y) -> f x y)
let numbers         = seq [1 .. 100]           |> Seq.map string
let fizz            = seq [“”;””;”fizz”]       |> cycle
let buzz            = seq [“”;””;””;””;”buzz”] |> cycle
let fizzBuzz        = zipWith (+) fizz buzz |> zipWith max numbers

fizzBuzz |> Seq.take 20 |> Seq.toList

Ausführbar auf http://tryfs.net/snippets/snippet-tl
Aufgenommen in http://fssnip.net/tl 

Keywords: FizzBuzz without modulo, FizzBuzz ohne Modulo