Use a terceira pilha
Se você leu o título, pode ficar um pouco confuso. Certamente existem apenas duas pilhas no Brain-Flak? No entanto, garanto que ele existe e é uma das ferramentas mais poderosas, se não a mais poderosa, para escrever e jogar no Brain-Flak.
O que é o "Third Stack"?
Todo programa Brain-Flak usa a terceira pilha de uma maneira ou de outra, mas a maior parte do uso é realizada nos bastidores e geralmente é útil simplesmente ignorar o fato de que ela existe. Cada parêntese no programa adiciona ou remove um item da pilha. Três dos chavetas abertos ([<
adicionam um item à pilha, enquanto os três conjugados )]>
removem um item da pilha. O valor do item na pilha é o valor do escopo atual do programa e o uso de nilads modificará esse valor de determinadas maneiras. O parêntese próximo )
tem a função exclusiva de mover um elemento da Terceira Pilha para a pilha atual; um empurrão.
Espero que isso esteja ficando claro para você. A Terceira Pilha é algum tipo de pilha que lembra os valores de retorno do código que já foi executado. Vamos seguir um exemplo de um programa simples, acompanhando as duas pilhas normais e a Terceira Pilha.
Exemplo
Seguiremos o seguinte programa. Este programa envia -3, 1, -2
para a pilha.
(([()()()])(()))
Começamos com três chaves abertas, que empurram o zero para a terceira pilha.
Nossas pilhas agora se parecem com isso, a Terceira Pilha é a da direita e a pilha ativa tem uma parte ^
inferior:
0
0
0 0 0
^
(([()()()])(()))
^
Agora temos três ()
niladas. Isso não faz nada com as duas pilhas normais, no entanto, cada uma adiciona uma ao topo da Terceira Pilha, fazendo com que nossas pilhas se pareçam com:
3
0
0 0 0
^
(([()()()])(()))
^
Agora encontramos um, ]
conforme declarado antes, que chaves fechadas removem um item da Terceira Pilha, mas ]
tem a função de subtrair o elemento que ele remove da parte superior da pilha. Assim, nossas novas pilhas terão a seguinte aparência:
-3
0 0 0
^
(([()()()])(()))
^
Isso faz sentido; [...]
negação ]
deve subtrair para baixo.
Agora devemos executar a )
. Como você provavelmente se lembra, )
é o local do programa em que as coisas são empurradas para a pilha, de modo que moveremos o topo da Terceira Pilha para a pilha atual, além disso, adicionaremos o -3
elemento ao próximo na terceira pilha.
-3 0 -3
^
(([()()()])(()))
^
Mais uma vez encontramos um de nossos três chavetas abertas, para adicionar outro elemento à nossa Terceira Pilha.
0
-3 0 -3
^
(([()()()])(()))
^
Como dissemos anteriormente ()
, aumentará o topo da nossa terceira pilha em um.
1
-3 0 -3
^
(([()()()])(()))
^
E )
moverá o topo da Terceira Pilha para a pilha ativa e adicionará para baixo
1
-3 0 -2
^
(([()()()])(()))
^
O último )
move a Terceira Pilha para a pilha ativa e, como não há elementos restantes na Terceira Pilha para adicionar, não faz mais nada.
-2
1
-3 0
^
(([()()()])(()))
^
O programa terminou, então finalizamos e produzimos.
Este exemplo tem como objetivo dar uma idéia do que a Terceira Pilha é e faz. Ele não inclui todas as operações, mas espero que você possa descobrir o que cada uma delas faz por conta própria. Se você ainda está com dificuldades, incluí uma "folha de dicas" na parte inferior desta resposta para ajudá-lo.
Tá, e daí?
Ok, agora você entende a Terceira Pilha, mas "E daí?" Você já o estava usando, mesmo que não o chamasse de "Terceiro Stack", como o pensamento em termos do Third Stack o ajuda a jogar golfe?
Vamos olhar para um problema. Você quer pegar o triângulo de um número . Esta é a soma de todos os números menores que n.
Uma abordagem pode ser criar um acumulador no offstack e adicioná-lo à medida que você faz a contagem regressiva. Isso cria um código que se parece com isso:
(<>)<>{(({}[()])()<>{})<>}{}<>({}<>)
Experimente Online!
Este código é bastante compacto e pode-se pensar que não pode ficar muito menor. No entanto, se o abordarmos do terceiro ponto de vista da pilha, fica claro que isso é grosseiramente ineficiente. Em vez de colocar nosso acumulador na pilha, podemos colocá-lo na terceira pilha com um (
e recuperá-lo no final que usamos )
. Mais uma vez, percorreremos todos os números, mas desta vez não precisamos fazer muito para incrementar nossa Terceira Pilha, o programa faz isso por nós. Isso se parece com:
({()({}[()])}{})
Experimente Online
Esse código tem menos da metade do tamanho da versão bastante bem elaborada que fizemos anteriormente. De fato, uma pesquisa no computador comprovou que esse programa é o programa mais curto possível que pode executar essa tarefa. Este programa pode ser explicado usando a abordagem "soma de todas as execuções", mas acho que é muito mais intuitivo e claro quando explicado usando uma abordagem da Terceira Pilha.
Quando uso a Terceira Pilha?
Idealmente, sempre que você começar a trabalhar em um novo problema no Brain-Flak, pense em como eu faria isso com a Terceira Pilha em mente. No entanto, como regra geral, sempre que você tiver que rastrear algum tipo de acumulador ou ter um total em execução, é uma boa ideia tentar colocá-lo na sua terceira pilha, em vez das duas pilhas reais.
Outro momento em que pode ser uma boa ideia considerar o uso da terceira pilha é quando você não tem espaço para armazenar algum valor nas outras duas pilhas. Isso pode ser particularmente útil quando você está fazendo manipulações em duas pilhas existentes e deseja salvar um valor para uso posterior sem precisar acompanhar onde está.
Limitações da terceira pilha
O Third Stack é muito poderoso de várias maneiras, mas vem com suas próprias limitações e desvantagens.
Em primeiro lugar, a altura máxima da pilha para a terceira pilha em qualquer ponto é determinada em tempo de compilação. Isso significa que, se você quiser usar uma quantidade de espaço na pilha, precisará alocar esse espaço ao escrever o programa.
Em segundo lugar, a terceira pilha não é de acesso aleatório. Isso significa que você não pode executar nenhuma operação com nenhum valor, exceto o valor mais alto. Além disso, você não pode mover valores na pilha (por exemplo, troque os dois primeiros elementos).
Conclusão
O Third Stack é uma ferramenta poderosa e eu consideraria essencial para todos os usuários do Brain-Flak. Demora um pouco para se acostumar e requer uma mudança na maneira como você pensa sobre programação no Brain-Flak, mas quando usado corretamente, faz toda a diferença entre um decente e um incrível quando se trata de golfe.
Folha de dicas
Aqui está uma lista de operações e como elas afetam a Terceira Pilha
Operation | Action
====================================================
(,[,< | Put a zero on top of the Third Stack
----------------------------------------------------
) | Add the top of the Third Stack to the
| second element and move it to the
| active stack
----------------------------------------------------
] | Subtract the top of the Third Stack
| from the second element and pop it
----------------------------------------------------
> | Pop the top of the Third Stack
----------------------------------------------------
() | Add one to the top of the Third Stack
----------------------------------------------------
{} | Pop the top of the active stack and
| add it to the top of the Third Stack
----------------------------------------------------
[] | Add the stack height to the Third
| Stack
----------------------------------------------------
<>,{,} | Nothing
{...}
retorna a soma de todas as iterações.<>,{,} | Nothing
Localizando módulo / restante
Encontrar n módulo m é uma das operações aritméticas básicas, importante para muitos desafios. Para os casos de m> 0 e n> = 0 , pode ser usado o seguinte fragmento de 46-byte. Ele assume que n está no topo da pilha ativa com m o próximo pressionado e os substitui por n mod m . Deixa o resto das pilhas intactas.
Esta versão anotada mostra o conteúdo da pilha em alguns pontos do programa.
;
separa as duas pilhas e.
marca a pilha ativa.fonte
{({}[()])<>}
), mas uma vez eu percebi isso ... Genius :-)Redundância Push-Pop
Este é um grande problema. Também é um pouco diferente.
A idéia é que, se você pressiona alguma coisa e a abre sem fazer nada, não deveria ter pressionado.
Por exemplo, se você quiser mover algo para o offstack e depois adicionar um, poderá escrever o seguinte código:
Isso pode ser mais simples assim:
O primeiro programa pega o item, move-o, pega-o novamente e adiciona um, enquanto o segundo faz os dois de uma só vez.
Esse exemplo foi simples, porém pode ficar muito mais complexo. Tomemos, por exemplo:
A redução é menos clara aqui, mas o quarto pop do programa pode ser reduzido com o segundo push, da seguinte forma:
Esta é a ferramenta mais poderosa do meu repertório de golfe, mas é preciso alguma prática para ser bom nisso. Depois de fazer isso por um tempo, você será capaz de identificá-lo quase instantaneamente.
fonte
({}<{}>)
?({}<{}>)
destrói o item completamente. No entanto, esses programas não foram realmente concebidos para serem ótimos apenas para destacar o princípio em funcionamento aqui.Otimize seus números inteiros
Inteiros são tediosos para representar no Brain-Flak. Felizmente, temos uma pergunta que ajudará o Golf a um número inteiro Brain-Flak para você. (Observe que a pergunta foi projetada para enviar o número inteiro para a pilha, portanto a redundância push-pop provavelmente se aplica a programas mais realistas.)
fonte
Empurre contadores de loop extra
Freqüentemente, você desejará fazer algo como
ou
Quando a entrada pode conter '0' ou o resultado da operação X pode dar um 0, isso é realmente inconveniente. Porque você precisará fazer:
Para executar X em cada elemento e depois
Para reverter a matriz novamente.
Pior ainda, se estivermos operando pares de elementos adjacentes, precisaremos fazer isso
([][()])
no lugar de([])
. Isso é realmente inconveniente. Aqui está o truque: Enquanto você faz X em cada elemento, pressione 1 na pilha alternativa logo acima daX(element)
. Então, enquanto você está revertendo, você pode simplesmente fazerIsso é 8 bytes mais curto; portanto, quando você levar em consideração o código extra para pressionar 1, acabará economizando de 4 a 6 bytes. Para um exemplo mais concreto, compare a tarefa de obter deltas de uma matriz. Sem esse truque, você precisaria de:
Para 62. Com esse truque, você terá
Para 58. Se usado da maneira correta (por exemplo, reverter primeiro e remover dois
([][()])
depois), isso pode economizar ainda mais em cenários específicos.fonte
Aproveite o nilad 'Stack-Height'
Especialmente em desafios de complexidade kolmogorov , ou desafios em que você sempre sabe o tamanho da entrada, você pode tirar vantagem da nilad 'Stack-Height'
[]
para criar números inteiros.Vamos trabalhar com isso com um desafio hipotético: saída
CAT
. A maneira não-golfista é usar o jogador inteiro online para empurrar 67, 65 e 84. Isso fornece:(Novas linhas para maior clareza). Isso é 88 bytes, e não é tão bom assim. Se, em vez disso, forçarmos as diferenças consecutivas entre os valores, podemos economizar muito. Então, nós envolvemos o primeiro número em um empurrão chamada, e subtrair 2:
Então, tomamos este código, envolvê-la em um empurrão chamada, e adicionar 19 para o final:
São 62 bytes, para um enorme golfe de 26 bytes!
Agora é aqui que podemos tirar proveito do nilad de altura da pilha. No momento em que começamos a pressionar 19, sabemos que já existem 2 itens na pilha, então
[]
avaliaremos como2
. Podemos usar isso para criar 19 em menos bytes. A maneira óbvia é mudar o interior()()()
para()[]
. Isso economiza apenas dois bytes. Com um pouco mais de ajustes, podemos pressionar 19 comIsso economiza 6 bytes. Agora estamos com 56.
Você pode ver essa dica sendo usada com muita eficácia nessas respostas:
Hello World V1
Hello World V2
Magic The Gathering, amigo ou inimigo
Saída O número inteiro ausente
Alfabeto diagonal (meu favorito pessoal)
fonte
CAT
programa realmente empurraTAC
: P[]
: preceder0
s com(<>)
s antes do seu código. Isso realmente funciona se você planeja enviar o código de maneira inversa, mas se você encontrar o número certo, poderá reduzir bastante o código. Um pouco exagerado exemplo onde eu acrescentar 60
s, o que me permite usar apenas como muitos[]
s como eu uso()
Use o wiki
Nós temos um wiki ! Tem algumas falhas, mas é um recurso útil. Ele possui listas de programas úteis e bem organizados que você pode colar no seu código. Se você quer fazer algo que acha que alguém poderia ter feito antes que haja uma boa chance de isso estar no wiki.
fonte
Melhor loop
Aqui está fácil:
Uma construção bastante comum é:
Onde você deseja fazer um loop n vezes, mas ainda assim manter o n. No entanto, isso pode ser escrito como:
para salvar 2 bytes.
Outro paradigma bastante comum é
O qual fará um loop e acumulará a pilha inteira. Devido a algumas matemáticas sofisticadas, é o mesmo que:
fonte
Verifique seus negativos
Às vezes, você pode jogar alguns bytes escolhendo estrategicamente o que negar com a
[...]
mônada.Um exemplo simples está em
[...]
s aninhados . Por exemplo,[()()()[()()]]
poderia ser apenas[()()()]()()
Digamos que você queira verificar se um valor é um dos colchetes iniciais
(<{[
. A tentativa inicial é empurrar a diferença entre cada caractere e o loop, subtraindo-oNo entanto, você pode economizar 4 bytes pressionando as versões negativas das diferenças!
Geralmente, isso não economiza muito, mas também custa muito pouco esforço para mudar o
[...]
ambiente. Esteja atento a situações em que você pode pressionar o negativo de um contador em vez do positivo para economizar no incremento várias vezes em vez de diminuir mais tarde. Ou trocando(a[b])
com([a]b)
para fazer a diferença entre dois números negativos em vez de positivos.fonte
<a<b>c>
-><abc>
e<a[b]c>
-><abc>
.