I’ve been learning f# recently, it is a very powerful and elegant language and hopefully we will be using it for real development in the future. One of the first things I tried was writing a version of the classic Fizz-Buzz interview question (for integers from 1 to 100 print Fizz-Buzz if divisible by 15, Fizz if divisible by 3 and Buzz if divisible by 5).
In an effort to massively overcomplicate a trivial question I ended up defining a set of rules that could be applied to arbitrary inputs. The definition of a rule was:
- //a rule evaluates something to true of false
- type Rule<'a> = 'a -> bool
I then set about implementing the mod 3 and mod 5 rules and hit a problem. Ideally we should be able to apply our rules to any integer type, but because of the well known problem of .Net lacking an INumeric/IIntegral interface or equivalent this wasn’t possible and I ended up with separate rules for each integer type:
- let inline ModThreeInt() = fun n -> n % 3 = 0
- let inline ModThreeLong() = fun n -> n % 3L = 0L
- let inline ModThreeBigInt() = fun n -> n % 3I = 0I
Not very satisfactory! I really wanted a generic ModThree function that can be applied to any number that supports the mod operator. In fact we can get this behaviour using some Peano style arithmetic:
- let inline Mod (d : 'a) (n : 'b)
- = let inline zero x = x - x
- let inline one x = x / x
- let mutable cb = zero n
- for _ in [one d..d] do
- cb <- cb + (one n)
- n % cb = zero n
(Note we must make the function inline so it is not given a concrete type signature on first use)
The key here is are the generic zero and one functions defined in the first two lines. These give a one and a zero of type of the value they are called with. With these defined we can then increment a variable of type ‘b until it is equal to the value passed in of type ‘a. Then we can mod our value of type ‘b. Of course this is hopelessly inefficient if we are modding by a large number.
- //yay, we can call either of our mod functions with either a BigInt or an int
- //and they take either bigints or ints, no explicit casting
- let inline ModThree() = Mod 3
- let inline ModFive() = ModG 5I
After writing this I actually discovered that F# has some built in GenericOne and GenericZero functions so we could rewrite our generic mod function as:
- let inline ModG (d : 'a) (n : 'b)
- = let mutable c = GenericZero<'b>
- for i in [GenericOne<'a>..d] do
- c <- c + GenericOne<'b>
- n % c = GenericZero<'b>
So there we have it, a small part of a hideously overcomplicated and inefficient FizzBuzz solution, interesting though.
- let inline buzzFuzzRules _ =
- [ (Some BuzzFuzz, ModThree() &&& ModFive());
- (Some Buzz, ModThree());
- (Some Fuzz, ModFive());
- (None, Default)
- ]
No comments:
Post a Comment