Underload é um tarpit semi-funcional baseado em pilha criado por ais523 . Recentemente, tentei jogar golfe, pois é uma linguagem surpreendentemente elegante.
Que dicas você tem para jogar golfe no Underload? (Uma dica por resposta)
Gosto que a única forma de fluxo de controle é um evalcomando, nunca vi uma linguagem assim antes.
ETHproductions
Respostas:
3
Use *para saída
Como você pode produzir deixando uma string na pilha , pode ser útil acumular a string usando, em *vez de com S. Digamos que seu desafio foi "pegar uma string e acrescentar um espaço", a maneira de fazer isso com a saída seria:
S( )S
A maneira de fazer isso *, por outro lado, é um byte mais curto:
( )*
O problema é que, se sua saída tiver muita acumulação, poderá custar bytes para lidar com o elemento de saída na pilha.
Use um dicionário de funções reutilizadas repetidamente
Se você precisar usar muito um pedaço de código, faz sentido armazenar esse código na pilha e duplicá-lo e avaliá-lo de vez em quando. Até agora, isso é apenas a programação normal do Underload. Infelizmente, manter um valor na pilha por um longo tempo é difícil e tende a fazer com que seu código fique detalhado, e isso é verdade mesmo que o valor seja uma função e não dados. Isso fica muito pior se você tiver várias funções que precisam ser reutilizadas repetidamente.
No tipo de programa maior que pode se beneficiar de várias funções reutilizadas, uma solução que você pode usar é criar uma função grande que atenda a qualquer um de seus objetivos, dependendo da forma como é chamada (com base no que está embaixo na pilha, ou através de usar mais chamando as sequências que apenas ^; uma função cuidadosamente escrito pode distinguir ^^a partir ^:^de ^*^partir ^~^, dando-lhe quatro distintas, seqüências bastante curtas). Você também pode armazenar outras coisas úteis, como seqüências de caracteres que você usa várias vezes, neste "dicionário". Observe que, se você usar o dicionário intensamente, pode fazer sentido torná-lo uma espécie de quine, empurrando uma cópia de si mesmo de volta para a pilha, para que você não precise copiá-lo manualmente com: poder usá-lo sem perder a capacidade de usá-lo no futuro.
Eu ficaria louco muito antes de ter um programa grande o suficiente no Underload para que isso se tornasse um problema: P
Esolanging Fruit
Certa vez, escrevi alguns exemplos na página esolangs de como fazer dicionários com minha ^!!!!^pesquisa de estilo preferida (que também usei em vários outros exemplos na página, especialmente na seção de minimização). Embora isso possa não dar a pesquisa mais curta.
Ørjan Johansen
2
Escolha formatos de dados especializados para as operações de que o problema precisa
Como um exemplo simples, a implementação mais comum de booleanos é !()para false (ou seja, número inteiro 0) e a sequência nula para true (ou seja, número 1), mas se você tiver um problema fortemente baseado no XOR lógico, isso poderá gerar mais É bom usar a string nula para false e ~true (esse formato de dados pode ser convertido em qualquer outro formato booleano usando (false)~(true)~^!e permite a implementação muito concisa *do XOR.
É possível levar esse princípio geral ainda mais longe e usar funções que seu programa precisará posteriormente como parte dos valores de seus dados; isso economiza a necessidade de armazenar as funções e os dados separadamente na pilha. Isso pode tornar o fluxo de controle um pouco mais confuso, mas, quando se joga golfe, a manutenção geralmente precisa ficar no banco de trás, e não é como se o Underload fosse tão utilizável de qualquer maneira.
Eu costumava usar (!)e (~!)para booleanos, mas seu caminho parece melhor.
Esolanging Fruit
2
Decremento "sujo"
A maneira funcionalmente pura de diminuir um numeral da Igreja é usar a função predecessora do cálculo lambda:
\n.n(\p.\z.z($(pT))(pT))(\z.z0[whatever you define the predecessor of 0 to be])
Onde 0 = \ x. \ Yy, T = \ x. \ Yx e $ é o sucessor.
Reescrito no Underload, são 28 bytes:
(!())~(!())~(!:(:)~*(*)*~)~^!
Está tudo bem, mas podemos explorar algumas das propriedades úteis do Underload, a saber, que :!e ()*do são não-ops. Isso significa que, para um número n, :ⁿ!!()()*ⁿ(onde cⁿé crepetido nvezes) produz n-1. Por exemplo, fazer isso para o numeral da igreja 3 gera o seguinte:
:::!!()()***
Removendo pares no-op, obtemos:
:*
Qual é 2.
Portanto, esta é a nova e mais curta operação predecessora:
Isso quebra em n = 0, no entanto. Se você precisar disso, (()~(:))~:(^!!())*~(*)~^** ainda será 3 bytes mais curto.
Ørjan Johansen
@ ØrjanJohansen Geralmente, você teria um caso especial para n = 0, porque com os números do Underload o decréscimo de 0 não faz sentido de qualquer maneira.
precisa saber é o seguinte
1
Colocar valores de pilha desnecessários no espaço do programa
Na verdade, a subcarga possui duas pilhas - a pilha de strings e a pilha de comandos que compõem o código-fonte. A ^instrução Underload permite mover as strings da pilha anterior para a última. Ao fazer isso, podemos economizar muita manipulação desnecessária de pilhas.
Por exemplo, digamos que temos (a)(b)(c)na pilha principal e gostaríamos de concatenar os dois elementos inferiores, ignorando (c), para obter (ab)(c). A maneira ingênua de fazer isso é girar a pilha para obter (c)(a)(b)e concantenar e trocar de volta:
a~a~*~a*^*~
Isto é mau. Usar a~a~*~a*^para girar a pilha dessa maneira é extremamente caro e deve ser evitado quando possível. Ao colocar (c)no espaço do programa, isso pode ser feito quatro bytes mais curto:
a(*)~*^
A idéia é seguir as instruções que você deseja executar e, em seguida, adicionar uma instrução para (c)retroceder no final e avaliar o resultado. Isso significa que não precisamos nos preocupar (c)até que seja adiada depois que terminarmos.
eval
comando, nunca vi uma linguagem assim antes.Respostas:
Use
*
para saídaComo você pode produzir deixando uma string na pilha , pode ser útil acumular a string usando, em
*
vez de comS
. Digamos que seu desafio foi "pegar uma string e acrescentar um espaço", a maneira de fazer isso com a saída seria:A maneira de fazer isso
*
, por outro lado, é um byte mais curto:O problema é que, se sua saída tiver muita acumulação, poderá custar bytes para lidar com o elemento de saída na pilha.
fonte
Use um dicionário de funções reutilizadas repetidamente
Se você precisar usar muito um pedaço de código, faz sentido armazenar esse código na pilha e duplicá-lo e avaliá-lo de vez em quando. Até agora, isso é apenas a programação normal do Underload. Infelizmente, manter um valor na pilha por um longo tempo é difícil e tende a fazer com que seu código fique detalhado, e isso é verdade mesmo que o valor seja uma função e não dados. Isso fica muito pior se você tiver várias funções que precisam ser reutilizadas repetidamente.
No tipo de programa maior que pode se beneficiar de várias funções reutilizadas, uma solução que você pode usar é criar uma função grande que atenda a qualquer um de seus objetivos, dependendo da forma como é chamada (com base no que está embaixo na pilha, ou através de usar mais chamando as sequências que apenas
^
; uma função cuidadosamente escrito pode distinguir^^
a partir^:^
de^*^
partir^~^
, dando-lhe quatro distintas, seqüências bastante curtas). Você também pode armazenar outras coisas úteis, como seqüências de caracteres que você usa várias vezes, neste "dicionário". Observe que, se você usar o dicionário intensamente, pode fazer sentido torná-lo uma espécie de quine, empurrando uma cópia de si mesmo de volta para a pilha, para que você não precise copiá-lo manualmente com:
poder usá-lo sem perder a capacidade de usá-lo no futuro.fonte
^!!!!^
pesquisa de estilo preferida (que também usei em vários outros exemplos na página, especialmente na seção de minimização). Embora isso possa não dar a pesquisa mais curta.Escolha formatos de dados especializados para as operações de que o problema precisa
Como um exemplo simples, a implementação mais comum de booleanos é
!()
para false (ou seja, número inteiro 0) e a sequência nula para true (ou seja, número 1), mas se você tiver um problema fortemente baseado no XOR lógico, isso poderá gerar mais É bom usar a string nula para false e~
true (esse formato de dados pode ser convertido em qualquer outro formato booleano usando(false)~(true)~^!
e permite a implementação muito concisa*
do XOR.É possível levar esse princípio geral ainda mais longe e usar funções que seu programa precisará posteriormente como parte dos valores de seus dados; isso economiza a necessidade de armazenar as funções e os dados separadamente na pilha. Isso pode tornar o fluxo de controle um pouco mais confuso, mas, quando se joga golfe, a manutenção geralmente precisa ficar no banco de trás, e não é como se o Underload fosse tão utilizável de qualquer maneira.
fonte
(!)
e(~!)
para booleanos, mas seu caminho parece melhor.Decremento "sujo"
A maneira funcionalmente pura de diminuir um numeral da Igreja é usar a função predecessora do cálculo lambda:
Onde 0 = \ x. \ Yy, T = \ x. \ Yx e $ é o sucessor.
Reescrito no Underload, são 28 bytes:
Está tudo bem, mas podemos explorar algumas das propriedades úteis do Underload, a saber, que
:!
e()*
do são não-ops. Isso significa que, para um númeron
,:ⁿ!!()()*ⁿ
(ondecⁿ
éc
repetidon
vezes) produz n-1. Por exemplo, fazer isso para o numeral da igreja 3 gera o seguinte:Removendo pares no-op, obtemos:
Qual é 2.
Portanto, esta é a nova e mais curta operação predecessora:
Este é 7 bytes mais curto.
fonte
(()~(:))~:(^!!())*~(*)~^**
ainda será 3 bytes mais curto.Colocar valores de pilha desnecessários no espaço do programa
Na verdade, a subcarga possui duas pilhas - a pilha de strings e a pilha de comandos que compõem o código-fonte. A
^
instrução Underload permite mover as strings da pilha anterior para a última. Ao fazer isso, podemos economizar muita manipulação desnecessária de pilhas.Por exemplo, digamos que temos
(a)(b)(c)
na pilha principal e gostaríamos de concatenar os dois elementos inferiores, ignorando(c)
, para obter(ab)(c)
. A maneira ingênua de fazer isso é girar a pilha para obter(c)(a)(b)
e concantenar e trocar de volta:Isto é mau. Usar
a~a~*~a*^
para girar a pilha dessa maneira é extremamente caro e deve ser evitado quando possível. Ao colocar(c)
no espaço do programa, isso pode ser feito quatro bytes mais curto:A idéia é seguir as instruções que você deseja executar e, em seguida, adicionar uma instrução para
(c)
retroceder no final e avaliar o resultado. Isso significa que não precisamos nos preocupar(c)
até que seja adiada depois que terminarmos.fonte
(*)~a*^
, o que eu acho que é um pouco mais compostável. Essencialmente~a*^
é odip
comando da Joy.