Consejos para guardar en un idioma de golf

16

Estoy escribiendo un lenguaje de golf.

¿Sugiere variables, pila (s), cintas, registros, etc. para almacenar en un lenguaje de código de golf? ¿Qué pasa con la entrada implícita?

Definiciones aproximadas:

  • Una variable es simplemente un nombre (generalmente un carácter largo en idiomas de golf) al que se le puede asignar un valor y luego recuperarlo con ese nombre.
  • Un registro es como una variable, pero tiene sus propios comandos (generalmente de un solo byte) para establecer / obtener el valor.
  • Una pila es una matriz / lista de valores de longitud variable, donde los valores agregados más recientemente (los valores "en la parte superior") son los que se están modificando.
  • Una cola es como una pila, excepto que los valores "en la parte inferior " son los que se están modificando.
  • Una cinta es una matriz / lista estática de valores donde cada valor tiene un índice. La principal diferencia entre una pila y una cinta es que los valores de una cinta se modifican in situ.

Agradecería conocer las ventajas y desventajas de cada opción para diferentes escenarios, y en general. Evite las opiniones y las declaraciones de respaldo con razonamiento.

programador 5000
fuente
1
Creo que debería incluir una definición de estos términos en su pregunta
Kritixi Lithos
2
@Fatalize Técnicamente, el método de almacenamiento no depende del tipo de lenguaje de golf que esté haciendo, el tipo de lenguaje de golf depende del método de almacenamiento ...
ETHproductions
1
@ETHproductions Son totalmente interdependientes.
Fatalize
1
Agregué algunas definiciones aproximadas de los diversos términos de almacenamiento, siéntase libre de editar o revertir si no le gustan.
ETHproductions
2
Existe una relación muy estrecha entre el método de almacenamiento y el tipo de lenguaje, pero creo que hay que tener en cuenta ambos. Por ejemplo, los lenguajes "imperativos" (aquellos que realizan sus instrucciones estrictamente de izquierda a derecha) pueden estar basados ​​en la pila (CJam, 05AB1E), en cinta (BrainF ***) o en algo completamente diferente (V, que utiliza una gran cadena 2D llamada "buffer", junto con algunos registros). También hay lenguajes basados ​​en prefijos (Pyth), lenguajes basados ​​en infijos (Japt, Pip y, básicamente, todos los idiomas principales), lenguajes basados ​​en enlaces (Jelly), etc., los cuales apenas utilizan ninguno de los métodos mencionados.
ETHproductions

Respuestas:

4

Todos los tipos de almacenamiento implican almacenar algo en un punto y recuperarlo más tarde. Para hacer esto en una sola operación, debe almacenar o recuperar automáticamente, y especificar la posición del valor almacenado en la otra operación.

Es decir, para el almacenamiento explícito, puede crear un operador para recuperar el enésimo valor calculado antes de esta operación, o volver a colocar el valor actual después de n operaciones. Alternativamente, puede usar la posición absoluta desde el inicio del programa, o hacer más cosas como eliminar algunos elementos automáticamente después de algunas operaciones (como en una pila). También puede realizar múltiples operadores, recuperando de diferentes copias del almacenamiento con o sin estas operaciones automáticas. Y debe tratar de hacer que el número máximo necesario para especificar en las operaciones sea razonablemente pequeño, de modo que pueda asignar un operador para cada número.

Pero en la mayoría de los casos, ni siquiera necesita un operador y el lenguaje lo hará implícitamente. Es entonces cuando necesita considerar un modelo más estandarizado como pilas o colas. El más exitoso por ahora parecía ser la programación tácita, que ni siquiera menciona el almacenamiento directamente.

Si desea diseñar un nuevo modelo de este tipo, puede intentar expandir las evaluaciones como un dag, y tratar de pensar en un dag predeterminado si no se especifica nada más. Lo más probable es que el valor predeterminado sea solo un árbol, excepto que varias hojas pueden estar vinculadas a la misma entrada. Por ejemplo, podría usar una cola para un árbol equilibrado, o una pila para un árbol profundo donde las hojas son en su mayoría constantes, o algo así como Jelly para un árbol profundo donde las hojas son en su mayoría copias de la entrada.

Pero tenga en cuenta que puede codificar la forma de un árbol binario en solo 2 bits por operador. Por lo tanto, si su idioma tiene menos de 64 operadores, puede ignorar los modelos tradicionales y simplemente codificar el árbol completo en los bits de repuesto (llámelos banderas combine_parent y below_leaf). Incluso si hay más operadores, podría hacer un valor predeterminado bastante bueno (como el modelo de Jelly) y 3 modificadores para cambiarlo.

Puede usar el mismo modelo para almacenamiento implícito y explícito por conveniencia, pero no tiene que hacerlo. Por ejemplo, podría usar una pila para el almacenamiento implícito, pero no muestre elementos en el almacenamiento explícito (o en otro almacenamiento explícito además del implícito). Es probable que no se llame pila en la documentación final, pero se entiende la idea.

Como referencia, el tamaño de la codificación perfecta de un árbol binario es el logaritmo de los números catalanes . Y el tamaño de la codificación perfecta de un dag "binario" es el logaritmo de A082161 , pero obviamente no es práctico. Esto supone que un operador con argumento diferente ordena dos operadores diferentes, agregando otro bit cuando no lo está.

A veces es posible que aún desee variables para los bucles. Es posible reescribir los bucles de otras maneras. Pero si realmente lo necesita, no use una construcción de 1 byte además de un nombre para definir la variable. a menos que solo esté utilizando los valores preinicializados, generalmente es más eficiente usar un indicador de 1 bit para especificar si está leyendo o escribiendo esta variable.

jimmy23013
fuente
7

¡Los sugiero a todos!

Más en serio, todos son útiles algunas veces, ¡y cuanto más mejor! La entrada implícita nunca es mala , solo tiene una bandera para apagarla. Las variables son útiles, por lo que no necesitan encontrarse en una pila o cinta; Lo mismo con los registros. Las pilas son útiles para almacenar datos, y también lo son las cintas. Recomiendo tratar de implementar múltiples, digamos una pila y registros, o una pila y variables, como GolfScript. Si puede hacer que cada función tenga un byte, entonces su idioma probablemente será efectivo en el golf, ya que puede aprovechar las ventajas de cada uno.

Un ejemplo:

  • Digamos que quiero tomar dos números como entradas y agregar sus longitudes de cadena
  • Las variables podrían ser mejores para esto (una pila podría no)
  • Código de ejemplo en GolfScript (con entrada implícita):

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

    Sin embargo, con las variables (sé que es más largo, simplemente no tiene que intercambiar lugares en la pila):

    ~,: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

Otra cosa que puede ser útil son las sobrecargas. Por ejemplo, si tiene una función de almacenamiento variable, tal vez podría usarse como una mónada (función de una entrada; no estoy seguro del término fuera de J / K / APL) para agregar a la pila o la cinta.

Ejemplo:

c12 # Stores "1" into register 2
c1] # Pushes "1" onto the stack ("]" is used to denote the end of the monad)
No hay nadie aquí
fuente
¿Tal vez si se llama a un argumento con los tipos incorrectos, se agrega a una cola que luego se usa para completar valores si la pila está vacía?
Esolanging Fruit
5

Sugeriría tener algo de almacenamiento rápidamente utilizable (de lo dado: cinta, cola, pila) y algo de almacenamiento permanente (variables, registros) para que las cosas no se interpongan mientras el programa está haciendo algo no relacionado. Diría que mucho más rara vez daría algo y dejaría más caracteres libres para obtener más instrucciones de 1 byte.

De las definiciones dadas, las que creo que funcionarían mejor serían una pila y registros.
Una pila, porque una cinta tiene que usar funciones solo para almacenar algo nuevo en ella, mientras que una pila debe tener funciones simples de inserción y extracción (generalmente incorporadas en los comandos). Registros, porque generalmente toman menos bytes en comparación con las variables y si necesita almacenar más de 2-4 cosas diferentes para algo, está haciendo algo mal.

No limite sus funciones solo a lo que sugieren sus nombres o definiciones, algunas funciones como put the 1st thing of the stack on top of the stackdefinitivamente pueden ayudar.

dzaima
fuente
5

Básicamente, hay dos tipos de almacenamiento que deben manejarse de manera diferente; acceso a valores generados recientemente y / o las entradas; y almacenamiento a largo plazo.

Para el almacenamiento a largo plazo, las variables parecen funcionar mejor; cualquier cosa con un número limitado de opciones no se escala (aunque los idiomas con esta propiedad, como Jelly, pueden funcionar bastante bien incluso en tareas de tamaño mediano). Sin embargo, no es necesario proporcionar nombres de variables al almacenar la variable, la mayoría de las veces; solo tiene un comando para almacenar un valor en la variable y autogenerar los nombres de acuerdo con un patrón predecible para que almacenar y recuperar el valor pueda ser un comando cada uno en casos simples. (Por ejemplo, podría tener comandos para restaurar la variable asignada más recientemente, la segunda más recientemente, la tercera más recientemente, y así sucesivamente, hasta un pequeño número fijo, más un comando general que tomó un argumento).

Para el almacenamiento a corto plazo, desea que sea lo más implícito posible. Casi todos los idiomas de golf que he visto conectan la salida de un comando a una entrada del siguiente, por defecto; la forma exacta en que se hace esto difiere de un idioma a otro, pero normalmente se trata de lo mismo. Sin embargo, la segunda entrada para un comando de 2 entradas es más interesante. Tomarlo de una pila funciona bien en los casos en que los valores solo se usan una vez, pero no se escala bien al reutilizar los valores. (Intente evitar las primitivas de manipulación de la pila; si tiene que recurrir a usarlas, está desperdiciando muchos bytes). Alternativamente, usar la entrada del usuario o una variable asignada recientemente como el segundo argumento implícito tiende a generar algunos ahorros de bytes en programas simples, aunque usted

En un lenguaje de golf en el que estoy trabajando en este momento, utilizo una combinación de un mecanismo muy barato (un solo bit ) para especificar la forma del árbol de análisis, rellenando automáticamente los argumentos faltantes de las entradas del usuario de forma predeterminada, y un punto de verificación enfoque que hace posible establecer el valor predeterminado para los argumentos faltantes en otra cosa (además de muchos casos especiales). En algún momento es probable que agregue variables para comunicar información a mayores distancias a lo largo del programa.


fuente
0

Sugiero una cinta y un registro.

Prefiero las cintas sobre las pilas porque las cintas tienden a tener menos elementos, lo que facilita su manipulación. Además, poder colocar elementos en la cinta en el registro y viceversa hace que el código sea fácil y corto.


fuente