Dicas para armazenamento em um idioma de golfe


16

Eu estou escrevendo uma linguagem de golfe.

Você sugere variáveis, pilha (s), fitas, registros, etc. para armazenamento em um idioma de código-golfe? E quanto à entrada implícita?

Definições aproximadas:

  • Uma variável é simplesmente um nome (geralmente um caractere em idiomas de golfe) ao qual um valor pode ser atribuído e posteriormente recuperado por esse nome.
  • Um registro é como uma variável, mas possui comandos próprios (geralmente de byte único) para definir / obter o valor.
  • Uma pilha é uma matriz / lista de valores de comprimento variável, em que os valores adicionados mais recentemente (os valores "na parte superior") são os que estão sendo modificados.
  • Uma fila é como uma pilha, exceto que os valores "na parte inferior " são os que estão sendo modificados.
  • Uma fita é uma matriz / lista estática de valores em que cada valor possui um índice. A principal diferença entre uma pilha e uma fita é que os valores em uma fita são modificados no local.

Gostaria de saber as vantagens e desvantagens de cada opção para diferentes cenários e em geral. Evite opiniões e declarações de backup com raciocínio.


1
Eu acho que você deveria incluir uma definição desses termos na sua pergunta
Kritixi Lithos

2
@Fatalize Tecnicamente, o método de armazenamento não é dependente de que tipo de golfe idioma que você está fazendo, o tipo de linguagem golfe é dependente do método de armazenamento ...
ETHproductions

1
@ETHproductions Eles são totalmente interdependentes.
Fatalize

1
Eu adicionei algumas definições aproximadas dos vários termos de armazenamento, fique à vontade para editar ou reverter se você não gostar.
ETHproductions

2
Existe uma relação muito estreita entre o método de armazenamento e o tipo de linguagem, mas acho que você precisa analisar os dois. Por exemplo, idiomas "imperativos" (aqueles que executam suas instruções estritamente da esquerda para a direita) podem ser baseados em pilha (CJam, 05AB1E), baseados em fita (BrainF ***) ou qualquer outra coisa totalmente (V, que usa uma grande string 2D chamada "buffer", juntamente com alguns registros). Existem também linguagens baseadas em prefixos (Pyth), linguagens baseadas em infixos (Japt, Pip e basicamente todos os idiomas principais), linguagens baseadas em link (Jelly) etc. etc., que dificilmente usam qualquer um dos métodos mencionados.
ETHproductions

Respostas:


4

Todos os tipos de armazenamento envolvem armazenar algo em um ponto e recuperá-lo mais tarde. Para fazer isso em apenas uma operação, você deve armazenar ou recuperar automaticamente e especificar a posição do valor armazenado na outra operação.

Ou seja, para armazenamento explícito, você pode criar um operador para recuperar o enésimo valor calculado antes desta operação ou adiar o valor atual após n operações. Como alternativa, você pode usar a posição absoluta desde o início do programa ou fazer mais coisas, como remover alguns elementos automaticamente após algumas operações (como em uma pilha). Você também pode criar vários operadores, recuperando cópias diferentes do armazenamento com ou sem essas operações automáticas. E você deve tentar tornar o número máximo necessário para especificar nas operações razoavelmente pequeno, para poder atribuir um operador para cada número.

Mas na maioria dos casos, você nem precisa de um operador e o idioma fará isso implicitamente. É quando você precisa considerar um modelo mais padronizado, como pilhas ou filas. O mais bem-sucedido no momento parecia a programação tácita, que nem menciona diretamente o armazenamento.

Se você deseja projetar um novo modelo, tente expandir as avaliações como um dag e tente pensar em um dag padrão, se nada mais for especificado. Provavelmente, o padrão é apenas uma árvore, exceto que várias folhas podem estar vinculadas à mesma entrada. Você poderia, por exemplo, usar uma fila para uma árvore equilibrada, ou uma pilha para uma árvore profunda, onde as folhas são principalmente constantes, ou algo como Jelly para uma árvore profunda, onde as folhas são principalmente cópias da entrada.

Mas observe que você pode codificar a forma de uma árvore binária em apenas 2 bits por operador. Portanto, se seu idioma tiver menos de 64 operadores, você poderá realmente ignorar os modelos tradicionais e apenas codificar a árvore completa nos bits sobressalentes (chame-os de sinalizadores combine_parent e below_leaf). Mesmo se houver mais operadores, você pode fazer um padrão bastante bom (como o modelo do Jelly) e 3 modificadores para alterá-lo.

Você pode usar o mesmo modelo para armazenamento implícito e explícito por conveniência, mas não precisa. Por exemplo, você pode usar uma pilha para armazenamento implícito, mas não pop elementos no armazenamento explícito (ou em outro armazenamento explícito, além do implícito). É provável que não seja chamado de pilha na documentação final, mas você entendeu.

Para referência, o tamanho da codificação perfeita de uma árvore binária é o logaritmo dos números catalães . E o tamanho da codificação perfeita de um dag "binário" é o logaritmo de A082161 , mas obviamente impraticável. Isso pressupõe que um operador com argumento diferente ordene dois operadores diferentes, adicionando outro bit quando não estiver.

Às vezes, você ainda pode querer variáveis ​​para os loops. Pode ser possível reescrever os loops de outras maneiras. Mas se você realmente precisar, não use uma construção de 1 byte, além de um nome para definir a variável. a menos que você esteja usando apenas os valores pré-inicializados, geralmente é mais eficiente usar um sinalizador de 1 bit para especificar se você está lendo ou gravando essa variável.


7

Eu sugiro todos eles!

Mais a sério, todos eles são úteis algumas vezes, e quanto mais, melhor! A entrada implícita nunca é ruim , basta ter um sinalizador para desativá-lo. As variáveis ​​são úteis para que não precisem ser encontradas em uma pilha ou fita; o mesmo com registros. As pilhas são úteis para armazenar dados, assim como as fitas. Eu recomendo tentar implementar várias, digamos uma pilha e registradores, ou uma pilha e variáveis, como o GolfScript. Se você pode transformar cada função em um byte, seu idioma provavelmente será eficaz no golfe, pois você pode usar as vantagens de cada um.

Um exemplo:

  • Digamos que eu queira pegar dois números como entradas e adicionar seus comprimentos de string
  • Variáveis ​​podem ser melhores para isso (uma pilha pode não)
  • Exemplo de código no GolfScript (com entrada implícita):

    ~,\,+
    ~ # Eval input (e.g. "1 2" > 1, 2)
    , # Take Length
    \ # Reverse items on the stack
    , # Take Length
    + # Add
      # Implicit output
    

    No entanto, com variáveis ​​(eu sei que é mais longo, ele simplesmente não precisa trocar de lugar na pilha):

    ~,:a;,:b;ab+
    ~ # Eval input
    , # Get length
    :a# Store in "a"
    ; # Discard value left on stack
    , # Length
    :b# Store in "b"
    ; # Discard
    a # Recall a
    b # Recall b
    + # Add
      # Implicit output
    

Sobrecargas

Outra coisa que pode ser útil são as sobrecargas. Por exemplo, se você tiver uma função de armazenamento variável, talvez possa ser usada como uma mônada (função de uma entrada; não tenho certeza do termo lado externo do J / K / APL) para adicionar à pilha ou fita.

Exemplo:

c12 # Stores "1" into register 2
c1] # Pushes "1" onto the stack ("]" is used to denote the end of the monad)

Talvez se um argumento for chamado com os tipos errados, ele será adicionado a uma fila que será usada para preencher valores se a pilha estiver vazia?
Esolanging Fruit

5

Eu sugeriria ter algum armazenamento rapidamente utilizável (do dado - fita, fila, pilha) e algum armazenamento permanente (variáveis, registros) para que as coisas não atrapalhem enquanto o programa está fazendo algo não relacionado. Eu diria que muito mais raramente daria algo e deixava mais caracteres livres para obter mais instruções de 1 byte.

A partir das definições dadas, as que eu acho que funcionariam melhor seriam uma pilha e registros.
Uma pilha, porque uma fita precisa usar funções apenas para armazenar algo novo nela, enquanto uma pilha deve ter funções simples de envio e envio (geralmente construídas nos comandos). Registradores, porque eles usam menos bytes geralmente em comparação com variáveis ​​e, se você precisar armazenar mais de 2 a 4 coisas diferentes, está fazendo algo errado.

Não limite as funções deles apenas ao que seus nomes ou definições sugerem - algumas funções como put the 1st thing of the stack on top of the stackpodem definitivamente ajudar.


5

Existem basicamente dois tipos de armazenamento que precisam ser tratados de maneira diferente; acesso a valores gerados recentemente e / ou entradas; e armazenamento a longo prazo.

Para armazenamento de longo prazo, as variáveis ​​parecem funcionar melhor; qualquer coisa com um número limitado de opções não é escalável (embora os idiomas com essa propriedade, como o Jelly, ainda assim possam funcionar razoavelmente bem, mesmo em tarefas de tamanho médio). No entanto, não há necessidade de fornecer nomes de variáveis ao armazenar a variável, na maioria das vezes; basta ter um comando para armazenar um valor na variável e gerar automaticamente os nomes de acordo com um padrão previsível, de modo que armazenar e recuperar o valor possa ser um comando em casos simples. (Por exemplo, você pode ter comandos para restaurar a variável atribuída mais recentemente, a segunda mais recentemente, a terceira mais recentemente e assim por diante, até um pequeno número fixo, além de um comando geral que utilizou um argumento.)

Para armazenamento de curto prazo, você deseja que o máximo de implícito possível. Quase todos os idiomas de golfe que eu vi conectar a saída de um comando a uma entrada do próximo, por padrão; a maneira exata pela qual isso é feito difere de idioma para idioma, mas normalmente chega à mesma coisa. No entanto, a segunda entrada para um comando de 2 entradas é mais interessante. Tirá-lo de uma pilha funciona bem nos casos em que os valores são usados ​​apenas uma vez, mas não escalam bem ao reutilizar valores. (Tente evitar primitivas de manipulação de pilha; se você precisar usá-las, estará gastando muitos bytes.) Como alternativa, usar a entrada do usuário ou uma variável atribuída recentemente como o segundo argumento implícito tende a economizar alguns bytes em programas simples, embora você

Em uma linguagem de golfe em que estou trabalhando no momento, uso uma combinação de um mecanismo muito barato (um único bit ) para especificar a forma da árvore de análise, preenchendo automaticamente os argumentos ausentes das entradas do usuário por padrão e um ponto de verificação abordagem que possibilita definir o padrão para argumentos ausentes para outra coisa (além de muitos casos especiais). Em algum momento, provavelmente adicionarei variáveis ​​para comunicar informações a maiores distâncias ao longo do programa.


0

Sugiro uma fita e um registro.

Prefiro fitas a pilhas, porque as fitas tendem a ter menos elementos, facilitando a manipulação deles. Além disso, a possibilidade de colocar elementos na fita no registro e vice-versa facilita o código abreviado.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.