Monday, January 4, 2016

Code Scribbling with Fizz Buzz

There's this thing that I like to do called code scribbling.  Code scribbling is about letting loose.  You noodle around with a piece of code for no particular reason.  Fun is the only goal here.  But it's an important goal.  Fun helps loosen you up for the important programming work you do.  The code scribbling session below is one such example of me just enjoying myself with a little Python 3.4.

A Code Scribbling Session


My mad plan here is to play a little Katamari Damacy with the Fizz Buzz problem.  I want to start simple and just add thing after thing until it turns into a big ball of chaos.  We'll start with a basic loop and modulus operators.  Here is that.


def fizzbuzz1(max_num):
for num in range(1, max_num+1):
msg = ""
if num % 3 == 0:
msg += "fizz"
if num % 5 == 0:
msg += "buzz"
print("{}: {}".format(num, msg))
view raw fizzbuzz1.py hosted with ❤ by GitHub


Let's shorten this up with some ternary operators.


def fizzbuzz2(max_num):
for num in range(1, max_num+1):
fizz = "fizz" if num % 3 == 0 else ""
buzz = "buzz" if num % 5 == 0 else ""
print("{}: {}".format(num, fizz + buzz))
view raw fizzbuzz2.py hosted with ❤ by GitHub


Okay, that was concise.  Time to make it more interesting.  What if the two ternary operators were concatenated together and thrown into a list comprehension?  The fizz and buzz ternary expressions have to be wrapped in their own sets of respective parenthesis.  This ensures that it evaluates correctly.  The "==" aspect of the modulus expression can be reduced to something more "truthy" so that it fits nicer in a blog post.  Here's what that looks like.


def fizzbuzz3(maxnum):
fizzbuzz_pairs = [(num,
("" if num % 3 else "fizz") + ("" if num % 5 else "buzz"))
for num in range(1, maxnum + 1)]
for num, msg in fizzbuzz_pairs:
print("{}: {}".format(num, msg))
view raw fizzbuzz3.py hosted with ❤ by GitHub


Interesting if a bit of a space eater.  A generator expression would make it lazier.  At the very least, it won't make the code look any worse than before.  Here's that code.


def fizzbuzz4(maxnum):
fizzbuzz_pairs = ((num,
("" if num % 3 else "fizz") + ("" if num % 5 else "buzz"))
for num in range(1, maxnum + 1))
for num, msg in fizzbuzz_pairs:
print("{}: {}".format(num, msg))
view raw fizzbuzz4.py hosted with ❤ by GitHub


What else could be done with this?  Here's an idea.  Those ternary expressions could be wrapped into a lambda function.  Here's the result of that.


def fizzbuzz5(maxnum):
fizzbuzz_pairs = ((num,
(lambda n: ("" if n % 3 else "fizz") +
("" if n % 5 else "buzz") )(num))
for num in range(1, maxnum + 1))
for num, msg in fizzbuzz_pairs:
print("{}: {}".format(num, msg))
view raw fizzbuzz5.py hosted with ❤ by GitHub


What else?  We could take the string formatting out of the print statement and put that in the expression.  Then, the for loop just has to loop through the resulting list of strings. 


def fizzbuzz6(maxnum):
fizzbuzz_pairs = ("{}: {}".format(num,
(lambda n: ("" if n % 3 else "fizz") +
("" if n % 5 else "buzz") )(num))
for num in range(1, maxnum + 1))
for msg in fizzbuzz_pairs:
print(msg)
view raw fizzbuzz6.py hosted with ❤ by GitHub


Hmm.  Come to think, why have the for loop at all?  The string join method could just join them all together with newline characters to ensure it prints like before.  Then, the print statement just has to print one big string all at once.


def fizzbuzz7(maxnum):
fizzbuzz_pairs = "\n".join("{}: {}".format(num,
(lambda n: ("" if n % 3 else "fizz") +
("" if n % 5 else "buzz") )(num))
for num in range(1, maxnum + 1))
print(fizzbuzz_pairs)
view raw fizzbuzz7.py hosted with ❤ by GitHub


Hooray, no more normal for loop!  But this is interesting.  That entire expression we have could be made into it's own lambda expression in and of itself.  You could even pass an argument and print the result of that.  Here's what that looks like.


def fizzbuzz8(maxnum):
pairs = (lambda mn: "\n".join("{}: {}".format(num,
(lambda n: ("" if n % 3 else "fizz") +
("" if n % 5 else "buzz") )(num))
for num in range(1, mn + 1)))(maxnum)
print(pairs)
view raw fizzbuzz8.py hosted with ❤ by GitHub


Okay, what's next?  There's no reason why we can't just move the entire expression and evaluation into the print statement itself.


def fizzbuzz9(maxnum):
print((lambda mn: "\n".join("{}: {}".format(num,
(lambda n: ("" if n % 3 else "fizz") +
("" if n % 5 else "buzz") )(num))
for num in range(1, mn + 1)))(maxnum))
view raw fizzbuzz9.py hosted with ❤ by GitHub


It's all one big blob now.  The print statement uses the expression here.  But wait!  There's no reason the print couldn't be a PART of the expression.


def fizzbuzz10(maxnum):
(lambda mn: print("\n".join("{}: {}".format(num,
(lambda n: ("" if n % 3 else "fizz") +
("" if n % 5 else "buzz") )(num))
for num in range(1, mn + 1))))(maxnum)
view raw fizzbuzz10.py hosted with ❤ by GitHub


Where do we take it from here?  Let's see.  Meta-programming perhaps?  There's nothing saying we can't create a nameless class with a __call__ method.  Then it's just a matter of making an instance.  From there, it's just a matter of taking advantage of __call__ and invoking the instance as if it was just another function. 


def fizzbuzz11(maxnum):
type("", (), {"__call__": lambda self, mn: print(
"\n".join("{}: {}".format(num,
(lambda n: ("" if n % 3 else "fizz") +
("" if n % 5 else "buzz") )(num))
for num in range(1, mn + 1)))})()(maxnum)
view raw fizzbuzz11.py hosted with ❤ by GitHub


This cake is baked.  Let's decorate!  I'll just wrap the __call__ function in a staticmethod decorator and make that troublesome self argument go away.


def fizzbuzz12(maxnum):
type("", (), {"__call__": staticmethod(lambda mn: print(
"\n".join("{:2}: {}".format(num,
(lambda n: ("" if n % 3 else "fizz") +
("" if n % 5 else "buzz") )(num))
for num in range(1, mn + 1))))})()(maxnum)
fizzbuzz12(50)
view raw fizzbuzz12.py hosted with ❤ by GitHub


Awesome!  All wrapped and decorated.  And we're done with this scribbling session.

What a Mess!

This is obviously not meant for production code.  This is essentially unfettered silliness enjoying some time in the sun.  Such is what creativity is.  It might accidentally lead to actual innovation but that's besides the point.  This is about taking some time to set your brain free.

So make it a priority this year to relax.  Have fun.  Scribble.