Suggerimenti per la conservazione in una lingua da golf


16

Sto scrivendo una lingua da golf.

Suggerisci variabili, stack (s), nastri, registri, ecc. Per l'archiviazione in un linguaggio code-golf? Che dire dell'input implicito?

Definizioni approssimative:

  • Una variabile è semplicemente un nome (di solito un carattere lungo nelle lingue del golf) a cui è possibile assegnare un valore e successivamente recuperarlo con quel nome.
  • Un registro è come una variabile, ma ha i suoi comandi (generalmente a byte singolo) per impostare / ottenere il valore.
  • Uno stack è un array / elenco di valori a lunghezza variabile, in cui i valori aggiunti più di recente (i valori "in alto") sono quelli che vengono modificati.
  • Una coda è come uno stack, tranne i valori "in fondo " sono quelli che vengono modificati.
  • Un nastro è un array / elenco statico di valori in cui ogni valore ha un indice. La differenza principale tra una pila e un nastro è che i valori su un nastro vengono modificati sul posto.

Gradirei conoscere i vantaggi e gli svantaggi di ciascuna opzione per diversi scenari e in generale. Si prega di evitare opinioni e dichiarazioni di backup con ragionamento.


1
Penso che dovresti includere una definizione di questi termini nella tua domanda
Kritixi Lithos

2
@Fatalize Tecnicamente il metodo di archiviazione non dipende dal tipo di linguaggio golf che stai creando, il tipo di linguaggio golf dipende dal metodo di archiviazione ...
ETHproductions

1
@ETHproductions Sono totalmente interdipendenti.
Fatalizza il

1
Ho aggiunto alcune definizioni approssimative dei vari termini di archiviazione, sentiti libero di modificare o ripristinare se non ti piacciono.
ETHproductions

2
Esiste una relazione molto stretta tra il metodo di archiviazione e il tipo di linguaggio, ma penso che sia necessario esaminare entrambi. Ad esempio, i linguaggi "imperativi" (quelli che eseguono le loro istruzioni rigorosamente da sinistra a destra) possono essere basati su stack (CJam, 05AB1E), basati su nastro (BrainF ***) o qualcos'altro (V, che utilizza una grande stringa 2D chiamata "buffer", insieme ad alcuni registri). Ci sono anche linguaggi basati su prefissi (Pyth), linguaggi basati su infissi (Japt, Pip, e praticamente ogni lingua tradizionale), linguaggi basati su link (Jelly), ecc. Che difficilmente usano nessuno dei metodi citati.
ETHproductions

Risposte:


4

Tutti i tipi di archiviazione comportano la memorizzazione di qualcosa in un punto e il recupero in un secondo momento. Per fare ciò in una sola operazione, è necessario eseguire la memorizzazione o il recupero automatico e specificare la posizione del valore memorizzato nell'altra operazione.

Cioè, per l'archiviazione esplicita, è possibile creare un operatore per recuperare l'ennesimo valore calcolato prima di questa operazione o ripristinare il valore corrente dopo n operazioni. In alternativa, è possibile utilizzare la posizione assoluta dall'inizio del programma o fare più cose come rimuovere alcuni elementi automaticamente dopo alcune operazioni (come in uno stack). È inoltre possibile creare più operatori, recuperando da diverse copie della memoria con o senza queste operazioni automatiche. E dovresti provare a rendere il numero massimo necessario per specificare nelle operazioni ragionevolmente piccolo, in modo da poter assegnare un operatore per ogni numero.

Ma nella maggior parte dei casi, non hai nemmeno bisogno di un operatore e la lingua lo farà implicitamente. Questo è quando è necessario considerare un modello più standardizzato come stack o code. Il più riuscito per ora sembrava essere la programmazione tacita, che non menziona nemmeno direttamente l'archiviazione.

Se si desidera progettare un nuovo modello di questo tipo, è possibile provare a espandere le valutazioni come dag e provare a pensare a un dag predefinito se non viene specificato nient'altro. Molto probabilmente, il valore predefinito è solo un albero, tranne per il fatto che più foglie possono essere collegate allo stesso input. Ad esempio, potresti usare una coda per un albero bilanciato, o una pila per un albero profondo dove le foglie sono per lo più costanti, o qualcosa come Jelly per un albero profondo dove le foglie sono per lo più copie dell'input.

Tuttavia, è possibile codificare la forma di un albero binario in soli 2 bit per operatore. Quindi, se la tua lingua ha meno di 64 operatori, puoi effettivamente ignorare i modelli tradizionali e codificare semplicemente l'albero completo nei bit di riserva (chiamali flags_parent e below_leaf). Anche se ci sono più operatori, potresti fare un default abbastanza buono (come il modello di Jelly) e 3 modificatori per cambiarlo.

È possibile utilizzare lo stesso modello per l'archiviazione implicita ed esplicita per comodità, ma non è necessario. Ad esempio, è possibile utilizzare uno stack per l'archiviazione implicita, ma non inserire elementi nella memoria esplicita (o in un'altra memoria esplicita oltre a quella implicita). È probabile che non verrà chiamato stack nella documentazione finale, ma avrai l'idea.

Per riferimento, la dimensione della codifica perfetta di un albero binario è il logaritmo dei numeri catalani . E la dimensione della codifica perfetta di un dag "binario" è il logaritmo di A082161 , ma ovviamente poco pratico. Questo presuppone un operatore con argomento diverso ordina due operatori diversi, aggiungendo un altro bit quando non lo è.

A volte potresti voler ancora variabili per i loop. Potrebbe essere possibile riscrivere i loop in altri modi. Ma se ne hai davvero bisogno, non usare un costrutto a 1 byte oltre a un nome per definire la variabile. a meno che non si utilizzino solo i valori preinizializzati, in genere è più efficiente utilizzare un flag da 1 bit per specificare se si sta leggendo o scrivendo questa variabile.


7

Consiglio a tutti loro!

Più seriamente, tutti tornano utili qualche volta, e tanto meglio è! L'input implicito non è mai male , basta avere una bandiera per spegnerlo. Le variabili sono utili, quindi non è necessario trovarle su una pila o su un nastro; lo stesso con i registri. Le pile sono utili per archiviare i dati, così come i nastri. Consiglio di provare a implementare più, ad esempio uno stack e registri, oppure uno stack e variabili, come GolfScript. Se riesci a rendere ogni funzione un byte, allora la tua lingua sarà probabilmente efficace nel golf, in quanto puoi usare i vantaggi di ciascuno.

Un esempio:

  • Supponiamo che io voglia prendere due numeri come input e aggiungere le loro lunghezze di stringa
  • Le variabili potrebbero essere migliori per questo (uno stack potrebbe non farlo)
  • Codice di esempio in GolfScript (con input implicito):

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

    Tuttavia, con le variabili (so che è più lungo, semplicemente non deve scambiare posti nello stack):

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

sovraccarico

Un'altra cosa che può essere utile sono i sovraccarichi. Ad esempio, se si dispone di una funzione di memorizzazione variabile, forse potrebbe essere utilizzata come monade (funzione a input singolo; non sono sicuro del termine lato esterno di J / K / APL) da aggiungere allo stack o al nastro.

Esempio:

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

Forse se un argomento viene chiamato con i tipi sbagliati, viene aggiunto a una coda che viene quindi utilizzata per compilare i valori se lo stack è vuoto?
Esolanging Fruit

5

Suggerirei di avere un po 'di spazio di archiviazione rapidamente utilizzabile (dal dato - nastro, coda, stack) e un po' di spazio di archiviazione permanente (variabili, registri) affinché le cose non si frappongano mentre il programma sta facendo qualcosa di non correlato. Direi che molto di più raramente darebbe qualsiasi cosa e lascerebbe più caratteri liberi per più istruzioni da 1 byte.

Dalle definizioni fornite, quelle che penso funzionerebbero meglio sarebbero una pila e registri.
Una pila, perché un nastro deve usare le funzioni solo per memorizzare una nuova cosa, mentre una pila dovrebbe avere semplici funzioni push e pop (di solito integrate nei comandi). Registra, perché di solito prendono meno byte rispetto alle variabili e se hai bisogno di memorizzare più di 2-4 cose diverse per qualcosa, stai facendo qualcosa di sbagliato.

Non limitare le loro funzioni solo a ciò che i loro nomi o definizioni suggeriscono attraverso - alcune funzioni come put the 1st thing of the stack on top of the stackpossono sicuramente aiutare.


5

Esistono sostanzialmente due tipi di archiviazione che devono essere gestiti in modo diverso; accesso a valori e / o input generati di recente; e archiviazione a lungo termine.

Per l'archiviazione a lungo termine, le variabili sembrano funzionare meglio; qualsiasi cosa con un numero limitato di opzioni non si ridimensiona (sebbene le lingue con questa proprietà, come Jelly, possano comunque fare abbastanza bene anche su compiti di medie dimensioni). Tuttavia, non è necessario fornire nomi di variabili durante la memorizzazione della variabile, la maggior parte delle volte; basta avere un comando per memorizzare un valore nella variabile e generare automaticamente i nomi secondo un modello prevedibile in modo che l'archiviazione e il recupero del valore possano essere un comando ciascuno in casi semplici. (Ad esempio, è possibile disporre di comandi per ripristinare l'ultima variabile assegnata, la seconda più recente, la terza più recente e così via, fino a un piccolo numero fisso, oltre a un comando generale che ha preso un argomento.)

Per l'archiviazione a breve termine, vuoi che il più possibile sia implicito. Quasi tutte le lingue del golf che ho visto collegano l'output di un comando a un input di quello successivo, per impostazione predefinita; il modo esatto in cui ciò avviene differisce da una lingua all'altra, ma normalmente arriva alla stessa cosa. Tuttavia, il secondo input per un comando a 2 input è più interessante. Prenderlo da uno stack funziona bene nei casi in cui i valori vengono utilizzati solo una volta, ma non si ridimensiona bene quando si riutilizzano i valori. (Cerca di evitare le primitive di manipolazione dello stack; se devi ricorrere al loro utilizzo, stai sprecando molti byte.) In alternativa, usando l'input dell'utente o una variabile assegnata di recente, poiché il secondo argomento implicito tende a risparmiare qualche byte su programmi semplici, sebbene tu

In un linguaggio di golf su cui sto lavorando al momento, utilizzo una combinazione di un meccanismo molto economico (un singolo bit ) per specificare la forma dell'albero di analisi, compilando automaticamente gli argomenti mancanti dagli input dell'utente per impostazione predefinita e un checkpoint approccio che consente di impostare il valore predefinito per gli argomenti mancanti su qualcos'altro (oltre a molti casi speciali). Ad un certo punto probabilmente aggiungerò variabili per comunicare informazioni a distanze maggiori lungo il programma.


0

Suggerisco un nastro e un registro.

Preferisco i nastri alle pile perché i nastri tendono ad avere meno elementi, facilitando la loro manipolazione. Inoltre, essere in grado di inserire elementi nel nastro nel registro e viceversa rende semplice il codice funzione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.