Suggerimenti per giocare a golf in DC


18

Quali consigli generali hai per giocare a golf in DC ?

dc è un'utility calcolatrice per UNIX / Linux che precede il linguaggio C. Sono interessato a come abbreviare i miei programmi in cc (calcoli?). Sto cercando idee che possano essere applicate al generale che siano almeno un po 'specifiche per il DC (es. Rimuovere i commenti non è una risposta utile)

Si prega di inviare un suggerimento per risposta.


7
Usa invece la Marvel.
Magic Octopus Urn

Risposte:


6

Istruzioni if-then-else

Supponiamo di voler controllare la condizione a==b(lasciate ae bsia memorizzata nei rispettivi registri con nome rispettivamente).

modificare:
[         # Everything is wrapped in one big macro
  [         # An inner macro for our *then* part
              # <-- Stuff to execute if a==b here
  2Q          # Then quit the inner and outer macro
]sE       # `E' is for Execution register ;)
la lb =E  # if a==b, execute E
          # if E is executed, it will quit the whole macro, so the rest is never reached:
          # <-- Stuff to execute if a!=b here
]x        # End macro; Execute

Sia (foo)un segnaposto, ai fini della condensazione:

[[(then)2Q]sE(condition)E(else)]x

Abbastanza sicuro, questa è la più compatta possibile istruzione (disponibile anche qui ).


1
Forse [[thenaction]P][[elseaction]P][r]sI 2 4 =I x sI fè un inizio? Le azioni per tehn e altro sono in pila, la Imacro " f" le scambia e viene assegnata condizionatamente. quindi la parte superiore dello stack verrà eseguita e la macro non utilizzata verrà rilasciata in I per ripulire lo stack. 2 4sono solo i dati di esempio da confrontare. In alternativa, la [x]sIparte può essere spostato al confronto, se considerato più leggibile: [[thenaction]P][[elseaction]P] 4 4 [r]sI =I x sI f. Gli fesempi mostreranno solo che lo stack è pulito in seguito ...

1
La pagina di Rosetta Code su Dc menziona 3 tipi di dce quella era la prima pagina in cui ho visto dcil if-then-elsecostrutto di OpenBSD . Penso che abbiamo bisogno di un dcbundle di fan con tutti e 3 i gusti per tutti i principali sistemi operativi ... o :-) ... e la mia if-then-elseproposta sopra non funziona sull'originale dcperché manca il rcomando ... :-(

1
Che dire: [[(if)2Q]si(condition)i(else)]x- avvolgendo il tutto in una macro e la porzione if all'interno di un'altra macro all'interno di quella, in modo da poter 2Quscire dall'intera cosa prima di raggiungere la porzione else. Quindi, se vuoi fare se 1 == 1 quindi stampa 1 altrimenti stampa 2 , sarebbe 1[[1P2Q]si1=i2P]x(non testato poiché non ho accesso a cc proprio qui e ora. Ero anche sicuro di aver fatto questo trucco in una risposta qui prima ma non riuscivo a trovarlo)
daniero,

Sì, ho fatto la matematica, il mio suggerimento è più breve. Con lo stesso esempio e "la notazione", e la rimozione di spazi bianchi è [/*else*/]sE[[/*then*/]sE]sIlalb=IlExcontro [[/*then*/2Q]sIlalb=I/*else*/]x- 6 byte differenza. Ancora non testato: P
daniero,

1
Bel lavoro, @daniero! Aggiornerò il post quando avrò tempo, oppure puoi farlo se vuoi.
Joe,

5

È possibile salvare l'input con d

Usando d, che duplica il ToS (parte superiore dello stack) è possibile spostare l'input di mezzo per un uso successivo, pur essendo ancora in grado di usarlo.


@NoOneIsHere oh cool !!! Grazie!
Rɪᴋᴇʀ

5

Array

Anche se sono un mal di testa per i principianti, dcoffre array. Funzionano così:

value index :a    # store `value' in array linked to top of stack `a', with index `index'
      index ;a    # push a[index] on (main) stack

Come al solito, il primo elemento ha l'indice 0. Le matrici possono essere utili quando si lavora con sequenze, come nella sequenza SUDSI , specialmente in combinazione con contatori. Le matrici possono ridurre la quantità di mescolamento dei numeri che è necessario eseguire (e il numero di contatori e confronti) se si desidera selezionare un elemento particolare senza distruggere l'ambiente. Ad esempio, se si desidera spostare una serie di numeri in un array, è possibile scrivere una funzione ricorsiva che utilizza z(profondità dello stack) o z 1-come indice, memorizza l'elemento e controlla se z == 0terminare se stesso.

[z 1- :a z 0 !=F]dsFx    # or I could just write such a function for you :)

Tieni presente quanto segue:

  • Le matrici sono associate ad istanze su pile nominate. Se si inserisce un nuovo valore su uno stack a cui è associato un array, anche quell'array verrà "respinto" e un "nuovo" array prenderà il suo posto. Il vecchio array non sarà utilizzabile fino a quando non sarà utilizzabile anche il valore corrispondente sullo stack denominato (ovvero, in cima allo stack). Questo è un concetto complicato che sarebbe meglio spiegato con una buona animazione, che è al di là di me.
  • È possibile memorizzare roba in un array senza in realtà spingendo un valore nel corrispondente registro di nome. Tuttavia, se lo fai, non puoi accedere allo stack / registrarti con quel nome per il resto della sessione. dcandrà in crash.
  • Se si elimina un valore da uno stack denominato, tutti i valori nell'array corrispondente andranno persi: nessun avviso, nessuna protezione, nulla. Appena andato (che può anche essere utile).

Bel lavoro con i consigli DC!
Rɪᴋᴇʀ

dcpotrebbe essere stato aggiornato di recente e il comportamento dell'array potrebbe essere leggermente cambiato per quanto riguarda l'arresto anomalo. Non posso confermare neanche adesso, ma penso che qualcosa sia stato diverso l'ultima volta che l'ho usato su Linux.
Joe,

1
Se si tenta di leggere un indice da un array che non è stato impostato, si ottiene 0 e non un errore. Che può essere molto utile, ma vale anche la pena tenere presente se stai potenzialmente mettendo 0 in array ... Avrai bisogno di un altro modo per verificare che l'indice sia stato toccato.
brhfl

5

0 all'ennesima potenza invece di condizionali / macro

A volte potresti aver bisogno di qualcosa come ternary condizionale:

A == B ? C : D;

Un buon modo per gestire questo è descritto nella risposta di @ Joe . Tuttavia possiamo fare di meglio:

0AB-^E*C+

dove E è D - C.

Questo verifica l'uguaglianza elevando 0 alla potenza della differenza dei due valori. Ciò comporta 1 se uguale e 0 altrimenti. Il resto dcriporta semplicemente 1 o 0 sui valori C o D. Questo funziona perché dà 0 0 = 1 e 0 n = 0 per n! = 1.


4

A volte è necessario scartare un numero dallo stack. Un modo per farlo è semplicemente inserirlo in una variabile non utilizzata, ad es st. Tuttavia, in alcune situazioni, è possibile visualizzarlo in un paio di altri punti, ad esempio la base di input quando non si dispone di più input numerici o nello specificatore di precisione se non si hanno più operazioni da fare in cui la precisione farebbe la differenza. Nel primo caso, utilizzare i. In quest'ultimo caso, utilizzare k.


Se l'output numerico non è importante, opuò essere utilizzato anche. E se una qualsiasi di queste cose non è importante, può essere utilizzata come memoria e come mero scarto - I/ K/ Orichiamale rispettivamente, e salva byte su sa/ laecc. Valori validi AFAIK: i2-16; kqualsiasi numero intero non negativo; oqualsiasi numero intero maggiore di 1.
brhfl

4

Lunghezza Calcolo: Z, X, ez

Zfa apparire ToS e invia il numero di cifre (decimale) se è un numero o il numero di caratteri se è una stringa. Ciò può essere utile per rilevare la lunghezza di un risultato (per l'output del buffering) o calcolare la lunghezza della stringa. Si noti che per i numeri, Zspinge la lunghezza combinata della parte intera e della parte della frazione.

Xapre il ToS e inserisce il numero di cifre nella parte della frazione del numero. Se ToS era una stringa, 0viene premuto.

Per trovare il numero di cifre nella parte intera del numero, si potrebbe usare dZrX-. Se non hai modificato la precisione dal valore predefinito k==0, l'utilizzo 1/Zè più breve, ma supponi di dover mantenere una particolare precisione diversa da zero dopo l'operazione: Kr0k1/Zrkè piuttosto un pugno nell'occhio.

zspinge il numero di oggetti in pila. Uno dei miei comandi preferiti, in realtà non contiene alcun valore! Potrebbe essere usato per generare una sequenza di numeri o incrementare un contatore. L'uso zdripetuto (diciamo all'inizio di una macro) potrebbe consentire di testare un calcolo su ciascun numero naturale o intero in ordine crescente.


Ho usato zper questo e quello prima, ma non mi è mai venuto in mente di usarlo come un trucco di un contatore ... Eccellente ...
brhfl

4

Cifre Aa Fpuò essere utilizzato in sostituzione per i numeri da 10 a 15. Tuttavia devono comunque essere trattati efficacemente come base di 10 cifre (supponendo di base di ingresso è 10) quando in luoghi diversi. In altre parole, con input base 10 FFnon rappresenterebbe 255, rappresenterebbe (15 * 10) + 15o 165.

Infatti questo funziona per tutte le cifre 0a Fin qualsiasi base di ingresso 2a 16. Quindi, se la base di input è 5, allora 26Esarebbe (2 * 5^2) + (6 * 5) + 14, o 94.

Nota che questo comportamento è in vigore per le fonti GNU non modificate. Tuttavia, come sottolinea @SophiaLechner, le distro basate su RedHat sembrano utilizzare bc-1.06-dc_ibase.patch che modifica questo comportamento in modo che le cifre> = ibase vengano trattate come ibase - 1, indipendentemente dal loro valore reale. Nota che il TIO dc sembra non avere bc-1.06-dc_ibase.patch (anche se è Fedora 28 ¯_ (ツ) _ / ¯).


Questo non è del tutto corretto - anche se le singole cifre sopra la base di input vengono interpretate come si spera, se il letterale ha più cifre o anche un punto decimale, le cifre non valide per la base vengono interpretate come (base-1). Quindi nella base di input 10 FFrappresenta 99, nella base di input 5 26Eè uguale a 244, cioè base 10 74.
Sophia Lechner,

@SophiaLechner Sei sicuro? tio.run/##S0n@/9/QIJ/L0CCTy82tgMs0k8vIzLXg/38A Quale dcversione stai utilizzando? Sto usando GNU DC 1.4.1 su Ubuntu e GNU DC 1.3 su MacOS
Digital Trauma

Interessante. Sto eseguendo 1.3.95 su Red Hat, ed ecco il tuo programma di esempio: [slechner @ XXX] $ dc -e '10o 10i FFp 5i 26Ep' 99 74 [slechner @ XXX] $ dc --version dc (GNU bc 1.06 .95) 1.3.95
Sophia Lechner,

Argh ... non può far funzionare il blocco di codice nei commenti. Il punto è che l' FFpoutput è 991.3.95. Potresti verificarlo nella tua versione di MacOS, allora?
Sophia Lechner,

1
Cosa certa! Grazie per tutta la ricerca.
Sophia Lechner,

2

Quando si inizializza una macro funzione (che useremo F) che si desidera eseguire immediatamente, utilizzare qualcosa di simile dsFxanziché sFlFx. Lo stesso vale per le variabili: dsapiuttosto che sala.

Se è necessario eseguire altre operazioni tra l'archiviazione e il caricamento (ad esempio, sa[other stuff]la), considerare sempre se quanto sopra è fattibile: se si lascia un valore nello stack prima delle altre operazioni, alla fine tornerà in cima alla fine di quelle operazioni?


2

L'ho scoperto per caso. Un altro modo per generare uno zero: _.

_è un segnale per indicare che le seguenti cifre sono un numero negativo. Esempio:

_3 # pushes -3

E se non lo seguissimo con un numero?

_ # pushes 0...sometimes

Funziona quando il successivo carattere non vuoto che segue il trattino basso non è una cifra. Se una cifra la segue, anche dopo una nuova riga, viene interpretata come un segno negativo.

c4 5_6  # -6,5,4
c4 5_ 6 # -6,5,4
c4 5_
6       # -6,5,4 # still a negative sign since the next thing it sees is a digit
c4 5_z  #  3,0,5,4 # if it's followed by a non-digit, it's a 0
c4 5_p6 #  6,0,5,4
c4 _*   #  0 # 4*0=0

1

Se il contenuto dell'intero stack deve essere stampato alla fine di un programma, è possibile utilizzare un ciclo macro ricorsivo per raggiungere questo obiettivo. Tuttavia, è molto più breve utilizzare semplicemente il fcomando.


1

dclegge l'input di una riga alla volta. Se devi leggere più elementi, farlo uno per riga richiede una ?lettura per ogni riga o un ciclo macro ingombrante. Invece, se tutti gli elementi di input possono essere posizionati su una riga separata ?dallo spazio, un singolo leggerà tutti gli elementi di input, spingendoli ciascuno nella pila.

Ad esempio in seq 10 | dc -e'?f', seqgenera numeri interi 1-10, uno per riga. il ?leggerà solo il primo 1che verrà emesso quando fscarica l'intero stack. Tuttavia seq 10 | tr '\n' ' ' | dc -e'?f', in , l' trintero input viene separato dallo spazio intero. In questo caso ?, leggerà tutti gli interi dalla riga in una volta sola e fli emetterà tutti.


1

Se un operatore è limitato dalla fonte, creane uno nuovo con a

Qualcosa che è tornato utile per me un paio di volte ora è evitare di usare un operatore specifico spingendo il valore ASCII dell'operatore, usando aper convertirlo in una stringa e sforzandolo in un registro per essere eseguito come macro in seguito su. Ad esempio, devo fare una divisione, ma non mi è permesso o cerco di evitare di usare il personaggio /. Posso, invece, fare 47asde poi in futuro quando dovrò dividere 16 per 4 16 4 ldx,.

  • Funzionerà solo per operatori a carattere singolo (non è possibile creare una stringa) e non funzionerà per comandi come quelli sche devono essere postfissi da qualcosa.
  • Questo aggiunge parecchi byte ed è quindi adatto solo quando è necessario evitare il carattere specifico o in qualche modo offre un bonus di punteggio.

1

Evitare gli spazi bianchi

Evitare gli spazi bianchi si presenta in alcune sfide ed è generalmente facile dc. Oltre stringhe, un tempo molto specifica che gli spazi bianchi diventa necessaria è quando si spinge più numeri consecutive: 1 2 3. Se questo deve essere evitato:

  • Eseguire una macro vuota tra: 1[]x2[]x3[]x.
  • Se parentesi sono fuori dal tavolo, memorizzare un NOP di un anticipo macro di tempo: 35asned eseguirlo è nel mezzo: 1lnx2lnx3lnx.

Puoi anche virgola numeri separati, se sei disposto a tollerare dc: ',' (054) unimplementedavvisi.
Trauma digitale il

Non ci avevo pensato - presumibilmente questo vale per qualsiasi dato token che non si risolve in un comando ... interessante ...
brhfl
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.