FizzBuzz

cs in code

FizzBuzz zná zřejmě každý programátor. Není to nic těžkého, implementace může vypadat třeba takto:

for index in range(1, 20):
    if index % 15 == 0:
        print 'FizzBuzz'
    elif index % 3 == 0:
        print 'Fizz'
    elif index % 5 == 0:
        print 'Buzz'
    else:
        print index

Ale to není sranda. Sice je to úloha na rychlé vyřazení špatných programátorů, ale paradoxně selhávají i schopní, protože se snaží na první dobrou napsat řešení nějak chytře (rozuměj one-linově). A nepovede se. Třeba:

for index in range(1, 20):
    print 'Fizz' * int(index % 3 == 0) + 'Buzz' * int(index % 5 == 0) or index

Ale to stále není sranda. Třeba takový Javista by mohl namítnout, že v tom je málo tříd…

class FizzBuzzNumber(int):
    def __str__(self):
        return (self.fizz + self.buzz) or super(FizzBuzzNumber, self).__str__()

    @property
    def fizz(self):
        return 'Fizz' if self.is_fizz else ''

    @property
    def buzz(self):
        return 'Buzz' if self.is_buzz else ''

    @property
    def is_fizz(self):
        return self % 3 == 0

    @property
    def is_buzz(self):
        return self % 5 == 0

for index in range(1, 20):
    print FizzBuzzNumber(index)

Mně to přišlo ale stále málo crazy. Když už mám třídu, proč si nevytvořit dynamicky třídu pro každé číslo. Když to jde, že?

class FizzBuzzNumberType(type):
    _class_cache = {}

    def __new__(mcs, name, bases, attributes):
        return mcs.create_instance

    @classmethod
    def create_instance(cls, number):
        return cls.create_or_get_class(number)(number)

    @classmethod
    def create_or_get_class(cls, number):
        if number not in cls._class_cache:
            cls._class_cache[number] = cls.create_class(number)
        return cls._class_cache[number]

    @classmethod
    def create_class(cls, number):
        printable_number = cls.get_printable_number(number)
        attributes = {
            '__str__': lambda self: printable_number,
        }
        new_cls = type.__new__(FizzBuzzNumberType, 'FizzBuzzNumber({})'.format(number), (int,), attributes)
        return new_cls

    @staticmethod
    def get_printable_number(number):
        return 'Fizz' * int(number % 3 == 0) + 'Buzz' * int(number % 5 == 0) or str(number)


class FizzBuzzNumber(int):
    __metaclass__ = FizzBuzzNumberType


for index in range(1, 20):
    print FizzBuzzNumber(index)

Tím sranda nemusí končit. Můžeme klidně pokračovat generováním kódu z XML. Aneb můžeme programovat aniž bychom museli měnit kód!

Ale to už je fakt za hranicí šílenosti. Pojďme zkusit něco jiného. Co třeba CSS?

.fizzbuzz {
    counter-increment: index;
}
.fizzbuzz:nth-of-type(n)::before{
    content: counter(index);
}
.fizzbuzz:nth-of-type(5n)::before{
    content: "";
}
.fizzbuzz:nth-of-type(3n)::before{
    content: "fizz";
}
.fizzbuzz:nth-of-type(5n)::after{
    content: "buzz";
}

Kam se hrabe JavaScript. :-)

Přemýšlel jsem, v čem ještě by šlo implementovat FizzBuzz. Vzpomněl jsem si na hlášku: Když se někteří lidé setkají s problémem, pomyslí si: „Já vím! Použiji regulární výrazy.“ V tom okamžiku mají problémy dva.

seq 1 19 | sed -r '3~3 s/[0-9]*/Fizz/; 5~5 s/[0-9]*$/Buzz/'

A taky že ano. První řešení je velice jednoduché. Tak ještě jednou bez počítání řádků!

seq 1 19 | sed -r 's/^([0369]|[258][0369]*[147]|[147]([0369]|[147][0369]*[258])*[258]|[258][0369]*[258]([0369]|[147][0369]*[258])*[258]|[147]([0369]|[147][0369]*[258])*[147][0369]*[147]|[258][0369]*[258]([0369]|[147][0369]*[258])*[147][0369]*[147])*$/Fizz\1/;s/^Fizz[0-9]*[05]$/FizzBuzz/; s/^Fizz[0-9]*$/Fizz/; s/^[0-9]*[05]$/Buzz/'

Ufff. Šlo by to napsat s menším počtem kroků, ale jeden konečný automat mi pro dnešek stačil.

Co vy, máte taky nějaký crazy FizzBuzz?

P.S.: Pro silnější nátury jsem narazil na oblíbený Brainfuck. A pro ověření si můžete udělat vlastní interpreter. :-)





You may also like