sexta-feira, 6 de outubro de 2023

FizzBuzz sem ifs

Uma questão que supostamente aparece em entrevistas para cargos de programação é o FizzBuzz. O enunciado é simples: imprima todos os números de 1 a n, mas imprima fizz para os múltiplos de 3, buzz para os múltiplos de 5, e fizzbuzz para os múltiplos de 3 e 5.

Sabemos que 3 e 5 são relativamente primos, então eles só vão juntos dividir n a cada 15 valores.

Um solução óbvia, então, é usar um pequeno mapeamento:


#!/usr/bin/perl
use strict;

for(1..99) {
  print [
    'fizzbuzz', 
    $_, 
    $_, 
    'fizz', 
    $_, 
    'buzz', 
    'fizz', 
    $_, 
    $_, 
    'fizz', 
    'buzz', 
    $_, 
    'fizz', 
    $_, 
    $_]->[$_%15]."\n";
}

Tudo bem, não tão óbvia como usar uma sequência de ifs.

Agora, não quero recriar o array dentro do loop a cada iteração, então vou tirá-lo e trocar os valores por funções. Algumas funções retornam o número que for passado como parâmetro, outras passam fizz, ou buzz, ou fizzbuzz.

Adicionei uma pequena otimização: o mapeamento é espelhado pela metade. O resultado do espelhamento tem uma posição extra, mas isso não incomoda.


#!/usr/bin/perl
use strict;

my @map=(
    sub {'fizzbuzz'},
    sub {shift},
    sub {shift},
    sub {'fizz'},
    sub {shift},
    sub {'buzz'},
    sub {'fizz'},
    sub {shift}
);
push @map, reverse @map;

for(1..99) {
  print $map[$_%15]->($_)."\n";
}

Com um pouquinho de mágica, podemos simplificar ainda mais. Resulta que o resto de n4 por 15 só produz os valores 0, 1, 6, e 10. Magicamente, podemos mapeá-los diretamente ao que queremos:


#!/usr/bin/perl
use strict;

my %map=(
    0 => sub {'fizzbuzz'},
    1 => sub {shift},
    6 => sub {'fizz'},
    10=> sub {'buzz'}
);

for(1..99) {
  print $map{$_**4%15}->($_)."\n";
}

Os detalhes dessa solução e a generalização para quaisquer outros números estão neste artigo.

Se alguém for usar uma solução dessas numa entrevista, recomendo explicar direitinho que não vai fazer isso com o código da empresa.

Nenhum comentário:

Postar um comentário