(Full source code for the entire series will be progressively published here, and the other days of the cavalcade are here: day 0, 1-8, 17-24)
FizzBuzz is a problem that maps rather well into functional programming (at
least as long as you don’t try to overcomplicate things), so
it is completely appropriate that we forego all state manipulations and refuse
to do anything with variables that could not be mapped directly to a
let ... in ...
construct.
In other words: what would happen if you got a bunch of Haskell academics and force fed them Go with a large spoon?
Answer: Much wailing and gnashing of teeth.
CompSci 232, Programming Paradigms. Data transformations only. Student is a bit puzzled about the hype around FP, but managed to come up with something which works and is not extra contrived.
package day09
import "fmt"
type IntString struct {
i int
s string
}
func FizzBuzz(i int) string {
return stringOf(buzzOf(fizzOf(IntString{i, ""})))
}
func fizzOf(a IntString) IntString {
if a.i%3 == 0 {
return IntString{a.i, a.s + "fizz"}
} else {
return a
}
}
func buzzOf(a IntString) IntString {
if a.i%5 == 0 {
return IntString{a.i, a.s + "buzz"}
} else {
return a
}
}
func stringOf(a IntString) string {
if len(a.s) == 0 {
return fmt.Sprint(a.i)
} else {
return a.s
}
}
Note: any similarities with Day 2 are purely coincidental.
Finalist undergrad student. Already has a thing for Haskell. Clearly wants to pursue an academic career (the poor soul). Actually solved the problem in Haskell first, then transpiled the code by hand with minor modifications.
package day10
import "fmt"
// tuple, (int, string)
type IntString struct {
i int
s string
}
// FizzBuzz :: int -> string
func FizzBuzz(i int) string {
return stringOf(buzzOf(fizzOf(IntString{i, ""})))
}
// fizzOf :: (int, string) -> (int, string)
func fizzOf(x IntString) IntString {
return xof(divisibleBy(3), "fizz")(x)
}
// buzzOf :: (int, string) -> (int, string)
func buzzOf(x IntString) IntString {
return xof(divisibleBy(5), "buzz")(x)
}
// xof :: (int -> bool) -> string -> ((int, string) -> (int, string))
func xof(check func(int) bool, rule string) func(IntString) IntString {
return func(a IntString) IntString {
if check(a.i) {
return IntString{a.i, a.s + rule}
} else {
return a
}
}
}
// stringOf :: (int, string) -> string
func stringOf(a IntString) string {
if len(a.s) == 0 {
return fmt.Sprint(a.i)
} else {
return a.s
}
}
// divisibleBy :: int -> (int -> bool)
func divisibleBy(n int) func(int) bool {
return func(i int) bool {
return i%n == 0
}
}
Master’s student. Totally at ease with FP. Desperately wants to curry. Is starting to question life choices.
package day11
import "fmt"
// Why aren't we doing this in Haskell, again?
// (int, string) tuple
type IntString struct {
i int
s string
}
// simulate deriving Show for IntString
func (x IntString) String() string {
if len(x.s) == 0 {
return fmt.Sprint(x.i)
} else {
return x.s
}
}
// FizzBuzz :: int -> string
func FizzBuzz(n int) string {
// looks rather stupid without `let`
// divisibleBy :: int -> (int -> bool)
divisibleBy := func(n int) func(int) bool {
return func(i int) bool {
return i%n == 0
}
}
// xof :: (int -> bool) -> string -> ((int, string) -> (int, string))
xof := func(check func(int) bool, rule string) func(IntString) IntString {
return func(x IntString) IntString {
if check(x.i) {
return IntString{x.i, x.s + rule}
} else {
return x
}
}
}
// currying? Would if I could.
// fizzOf :: (int, string) -> (int, string)
fizzOf := xof(divisibleBy(3), "fizz")
// buzzOf :: (int, string) -> (int, string)
buzzOf := xof(divisibleBy(5), "buzz")
return buzzOf(fizzOf(IntString{n, ""})).String()
}
PhD student. Wants to bring in the big Algebraic Data Type guns, and shoots himself in the foot doing so. Is really questioning some life choices. Can’t even find time to curry, much less anyone to do it with.
package day12
import "fmt"
// This is just silly. We could have used Rust.
// Let's pretend we have monads, destructuring, pattern matching
// and data constructors.
// data FB = AFizz a | ABuzz a | AFizzBuzz a | APlain a
type FB interface {
Plain() int
String() string
}
type APlain struct {
p int
}
func (p APlain) Plain() int {
return p.p
}
func (p APlain) String() string {
return fmt.Sprint(p.p)
}
type AFizz struct {
APlain
}
func (f AFizz) String() string {
return "fizz"
}
type ABuzz struct {
APlain
}
func (b ABuzz) String() string {
return "buzz"
}
type AFizzBuzz struct {
APlain
}
func (f AFizzBuzz) String() string {
return "fizzbuzz"
}
func FizzBuzz(i int) string {
return buzz(fizz(APlain{i})).String()
}
// really, I'm going to ask my advisor why he's an idiot.
func fizz(x FB) FB {
mod3 := x.Plain()%3 == 0
if mod3 {
switch x.(type) {
case APlain:
return AFizz{APlain{x.Plain()}}
case ABuzz:
return AFizzBuzz{APlain{x.Plain()}}
default:
return x
}
} else {
return x
}
}
func buzz(x FB) FB {
mod5 := x.Plain()%5 == 0
if mod5 {
switch x.(type) {
case APlain:
return ABuzz{APlain{x.Plain()}}
case AFizz:
return AFizzBuzz{APlain{x.Plain()}}
default:
return x
}
} else {
return x
}
}
// oh god, kill me now.
PhD advisor. The kind that berates students for not knowing subjects he is
paid to teach. Fervent believer of the “hair-shirt approach”. Is considering
asking next year’s students to implement a purely functional virtual machine.
Someone should tell him about this. And remind him that decrement
is
not a good primitive in terms of CPU cycle usage.
package day13
import "fmt"
// my master's and phd students are dense. It's just one of those batches. If
// they got off imgur I'm sure their output would improve, but cat pictures
// are a good use of student loans in 2018... The point of the exercise was
// to understand exactly how much effort is required to translate functional
// features to a non functional language. Translating Haskell to Haskell is
// not a good use of grants. Nimrods.
type (
// data FB = Fizz FB | Buzz FB | Just a
FB interface {
Value() int
}
Just struct {
i int
}
Buzz struct {
fb FB
}
Fizz struct {
fb FB
}
)
// make constructors part of data class
func (j Just) Value() int { return j.i }
func (b Buzz) Value() int { return b.fb.Value() }
func (f Fizz) Value() int { return f.fb.Value() }
// buzz :: int -> FB
func buzz(n int) FB {
if n%5 == 0 {
return Buzz{Just{n}}
} else {
return Just{n}
}
}
// fizz :: FB -> FB
func fizz(fb FB) FB {
if fb.Value()%3 == 0 {
return Fizz{fb}
} else {
return fb
}
}
// show :: FB -> string -> string
func show(fb FB, prev string) string {
switch v := fb.(type) {
case Just:
if len(prev) != 0 {
return prev
} else {
return fmt.Sprint(v.i)
}
case Fizz:
return show(v.fb, prev+"fizz")
case Buzz:
return show(v.fb, prev+"buzz")
default:
return "" // never happens, only makes compiler happy
}
}
func FizzBuzz(n int) string {
return show(fizz(buzz(n)), "")
}
Tenured professor. The kind that berates other professors for berating students.
package day14
import "fmt"
// Note to Dr. Ethelbert:
// I am not sure the students are the problem.
func FizzBuzz(n int) string {
return IfThenElse(
Cond(n%3 == 0),
IfThenElse(
Cond(n%5 == 0),
Value("fizzbuzz"),
Value("fizz"),
),
IfThenElse(
Cond(n%5 == 0),
Value("buzz"),
ToString(n),
),
).Solve()
}
// Haskell is for wimps. For a true hair-shirt experience, you should
// implement a (crude) lambda calculus interpreter instead.
type Calc struct {
f func() string
}
func (c Calc) Solve() string {
return c.f()
}
func True(x, _ Calc) Calc {
return x
}
func False(_, y Calc) Calc {
return y
}
func IfThenElse(c func(Calc, Calc) Calc, t, f Calc) Calc {
return c(t, f)
}
// Go->Lambda and Lambda->Go transformations
func Cond(b bool) func(Calc, Calc) Calc {
return map[bool]func(Calc, Calc) Calc{true: True, false: False}[b]
}
func Value(s string) Calc {
return Calc{func() string {
return s
}}
}
func ToString(n int) Calc {
return Calc{func() string {
return fmt.Sprint(n)
}}
}
// Oh, and we need to talk about grant allocations...
Self taught l33t h4x0r. Is into cryptography and applied math. Noticed that the
outputs of FizzBuzz(n)
are cyclical with a period of 15 over the input
integers, and proceeded to abuse Go arrays on that basis.
Also wants to remind us not to use (3,5)
as the semiprimes for an RSA private
key.
Got it.
Fun fact: at the time of this writing, there were at least 36 RSA Moduli all
over the web with 3
as a prime factor. No need for anti-encryption
laws, people.
package day15
import "fmt"
func FizzBuzz(n int) string {
x := [15]string{
"fizzbuzz", "", "", "fizz", "",
"buzz", "fizz", "", "", "fizz",
"buzz", "", "fizz", "", "",
}[n%15]
if x == "" {
return fmt.Sprint(n)
} else {
return x
}
}
Another, more experient, uber-l33t h4x0r. Is into all sorts of things,
including your home router. Thinks flow control is for sissies and prefers
to abuse first class functions and arrays in order to avoid
writing one if
statement. Likes to code-golf in APL, swap variables
without a tempvar and mine various
cryptocurrencies via JS snippets distributed through your niche WordPress blog.
package day16
import "fmt"
func fb(_ int) string { return "fizzbuzz" }
func f(_ int) string { return "fizz" }
func b(_ int) string { return "buzz" }
func s(n int) string { return fmt.Sprint(n) }
func FizzBuzz(n int) string {
return [15]func(int) string{
fb, s, s, f, s,
b, f, s, s, f,
b, s, f, s, s,
}[n%15](n)
}
Tune in next week for the ultimate sacrilege: Object Oriented FizzBuzzes, and a couple of extras.