Golfscript - 12 caracteres
{,1\{)*}/}:f
Introdução ao Golfscript - Fatorial passo a passo
Aqui está algo para as pessoas que estão tentando aprender golfe. O pré-requisito é um entendimento básico do golfscript e a capacidade de ler a documentação do golfscript.
Então, queremos experimentar nossa nova ferramenta golfscript . É sempre bom começar com algo simples, então estamos começando com fatorial. Aqui está uma tentativa inicial, baseada em um simples pseudocódigo imperativo:
# pseudocode: f(n){c=1;while(n>1){c*=n;n--};return c}
{:n;1:c;{n 1>}{n c*:c;n 1-:n;}while c}:f
Espaço em branco é muito raramente usado no golfe. O truque mais fácil para se livrar do espaço em branco é usar diferentes nomes de variáveis. Cada token pode ser usado como uma variável (consulte a página de sintaxe ). Fichas úteis para usar como variáveis são caracteres especiais como |
, &
, ?
- geralmente não qualquer coisa usada em outras partes do código. Estes são sempre analisados como tokens de caracteres únicos. Por outro lado, variáveis como n
exigirão um espaço para empurrar um número para a pilha depois. Os números são essencialmente variáveis pré-inicializadas.
Como sempre, haverá declarações que podemos mudar, sem afetar o resultado final. Em golfscript, tudo é avaliada como verdadeira, exceto 0
, []
, ""
e {}
(ver este ). Aqui, podemos alterar a condição de saída do loop para simplesmente {n}
(fazemos um loop adicional e terminamos quando n = 0).
Como no golfe em qualquer idioma, ajuda a conhecer as funções disponíveis. Felizmente, a lista é muito curta para o golfscript. Nós podemos mudar 1-
para (
salvar outro personagem. No momento, o código se parece com o seguinte: (poderíamos estar usando em 1
vez de |
aqui, se quiséssemos, o que eliminaria a inicialização.)
{:n;1:|;{n}{n|*:|;n(:n;}while|}:f
É importante usar bem a pilha para obter as soluções mais curtas (praticar praticar praticar). Geralmente, se os valores forem usados apenas em um pequeno segmento de código, talvez não seja necessário armazená-los em variáveis. Ao remover a variável de produto em execução e simplesmente usar a pilha, podemos salvar muitos caracteres.
{:n;1{n}{n*n(:n;}while}:f
Aqui está outra coisa para pensar. Estamos removendo a variável n
da pilha no final do corpo do loop, mas depois a pressionamos imediatamente. De fato, antes do início do loop, também o removemos da pilha. Em vez disso, devemos deixá-lo na pilha e podemos manter a condição do loop em branco.
{1\:n{}{n*n(:n}while}:f
Talvez possamos até eliminar completamente a variável. Para fazer isso, precisaremos manter a variável na pilha o tempo todo. Isso significa que precisamos de duas cópias da variável na pilha no final da verificação de condição para não perdê-la após a verificação. O que significa que teremos um redundante 0
na pilha após o término do loop, mas isso é fácil de corrigir.
Isso nos leva à nossa while
solução ideal de loop!
{1\{.}{.@*\(}while;}:f
Agora ainda queremos tornar isso mais curto. O alvo óbvio deve ser a palavra while
. Olhando para a documentação, existem duas alternativas viáveis - desdobrar e fazer . Quando você pode escolher diferentes rotas a seguir, tente pesar os benefícios de ambas. Desdobrar é 'praticamente um loop while', portanto, como uma estimativa, reduziremos o caractere 5 while
em 4 /
. Quanto a do
isso, cortamos while
3 caracteres e fundimos os dois blocos, o que pode salvar outro ou dois caracteres.
Na verdade, há uma grande desvantagem em usar um do
loop. Como a verificação da condição é feita depois que o corpo é executado uma vez, o valor de 0
estará errado, portanto, podemos precisar de uma instrução if. Vou lhe dizer agora que o desdobramento é mais curto (algumas soluções do
são fornecidas no final). Vá em frente e tente, o código que já temos exige alterações mínimas.
{1\{}{.@*\(}/;}:f
Ótimo! Nossa solução agora é super curta e terminamos aqui, certo? Não. São 17 caracteres e J possui 12 caracteres. Nunca admita derrota!
Agora você está pensando com ... recursão
Usar recursão significa que devemos usar uma estrutura de ramificação. Lamentável, mas como fatorial pode ser expresso de forma tão sucinta e recursivamente, isso parece ser uma alternativa viável à iteração.
# pseudocode: f(n){return n==0?n*f(n-1):1}
{:n{n.(f*}1if}:f # taking advantage of the tokeniser
Bem, isso foi fácil - se tivéssemos tentado a recursão antes, talvez nem tivéssemos olhado usando um while
loop! Ainda assim, temos apenas 16 caracteres.
Matrizes
Matrizes geralmente são criadas de duas maneiras - usando os caracteres [
e ]
, ou com a ,
função Se executado com um número inteiro no topo da pilha, ,
retorna uma matriz desse comprimento com arr [i] = i.
Para iterar sobre matrizes, temos três opções:
{block}/
: empurrar, bloquear, empurrar, bloquear, ...
{block}%
: [push, block, push, block, ...] (isso tem algumas nuances, por exemplo, valores intermediários são removidos da pilha antes de cada push)
{block}*
: empurrar, empurrar, bloquear, empurrar, bloquear, ...
A documentação do golfscript tem um exemplo de uso {+}*
para somar o conteúdo de uma matriz. Isso sugere que podemos usar {*}*
para obter o produto de uma matriz.
{,{*}*}:f
Infelizmente, não é tão simples assim. Todos os elementos estão desativados por um (em [0 1 2]
vez de [1 2 3]
). Podemos usar {)}%
para corrigir esse problema.
{,{)}%{*}*}:f
Bem, não exatamente. Isso não lida com zero corretamente. Podemos calcular (n + 1)! / (N + 1) para corrigir isso, embora isso custe demais.
{).,{)}%{*}*\/}:f
Também podemos tentar manipular n = 0 no mesmo bloco que n = 1. Isso é extremamente curto, tente e trabalhe o mais curto possível.
Não tão boa é a classificação, a 7 caracteres: [1\]$1=
. Observe que essa técnica de classificação tem objetivos úteis, como impor limites a um número (por exemplo, `[0 \ 100] $ 1 =)
Aqui está o vencedor, com apenas 3 caracteres:.! +
Se queremos ter o incremento e a multiplicação no mesmo bloco, devemos iterar todos os elementos da matriz. Como não estamos construindo uma matriz, isso significa que devemos usá-la {)*}/
, o que nos leva à implementação mais curta do fatorial! Com 12 caracteres, isso está associado a J!
{,1\{)*}/}:f
Soluções bônus
Começando com uma if
solução direta para um do
loop:
{.{1\{.@*\(.}do;}{)}if}:f
Podemos extrair mais alguns disso. Um pouco complicado, então você terá que se convencer de que esses funcionam. Certifique-se de entender tudo isso.
{1\.!!{{.@*\(.}do}*+}:f
{.!{1\{.@*\(.}do}or+}:f
{.{1\{.@*\(.}do}1if+}:f
Uma alternativa melhor é calcular (n + 1)! / (N + 1), o que elimina a necessidade de uma if
estrutura.
{).1\{.@*\(.}do;\/}:f
Mas a do
solução mais curta aqui leva alguns caracteres para mapear de 0 a 1 e tudo o mais para si - portanto, não precisamos de nenhuma ramificação. Esse tipo de otimização é extremamente fácil de perder.
{.!+1\{.@*\(.}do;}:f
Para qualquer pessoa interessada, são fornecidas aqui algumas soluções recursivas alternativas com o mesmo comprimento acima:
{.!{.)f*0}or+}:f
{.{.)f*0}1if+}:f
{.{.(f*}{)}if}:f
* Nota: Na verdade, eu não testei muitos dos trechos de código neste post, portanto, fique à vontade para informar se há erros.