Tipps zum Golfen in dc

18

Welche allgemeinen Tipps haben Sie zum Golfen in DC ?

dc ist ein Taschenrechner-Dienstprogramm für UNIX / Linux, das älter als die Sprache C ist. Ich bin daran interessiert, wie ich meine DC-Programme (Berechnungen?) Verkürzen kann. Ich suche nach Ideen, die auf allgemeines angewendet werden können, die zumindest ein bisschen spezifisch für DC sind (z. B. das Entfernen von Kommentaren ist keine hilfreiche Antwort).

Bitte posten Sie einen Tipp pro Antwort.

mriklojn
quelle
7
Verwenden Sie stattdessen Marvel.
Magic Octopus Urn

Antworten:

6

If-then-else-Anweisungen

Angenommen, wir möchten die Bedingung überprüfen a==b(lassen Sie aund bin ihren jeweils benannten Registern gespeichert werden).

bearbeiten:
[         # 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

Sei (foo)ein Platzhalter zum Verdichten von:

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

Ich bin mir ziemlich sicher, dass dies die kompakteste if-Anweisung ist (wird auch hier vorgestellt ).

Joe
quelle
1
Vielleicht [[thenaction]P][[elseaction]P][r]sI 2 4 =I x sI fist ein Anfang? Die Aktionen für tehn und else sind auf dem Stack, das " If" -Makro tauscht sie aus und wird bedingt aufgerufen. Dann wird das obere Ende des Stapels ausgeführt und das nicht verwendete Makro in I abgelegt, um den Stapel zu bereinigen. 2 4sind nur die beispieldaten zum vergleichen. Alternativ kann das [x]sIkann Teil zum Vergleich bewegt werden, wenn mehr lesbar betrachtet: [[thenaction]P][[elseaction]P] 4 4 [r]sI =I x sI f. Die fin den Beispielen soll nur zeigen, dass der Stack danach sauber ist ...
1
Rosetta - Code Seite über Dc erwähnt 3 Aromen dcund dass die erste Seite war , wo ich sah OpenBSD dc‚s - if-then-elseKonstrukt. Ich denke, wir brauchen ein dcFan Bundle mit allen 3 Varianten für alle gängigen Betriebssysteme ... o :-) ... und mein if-then-elseVorschlag oben funktioniert nicht auf dem Original, dcweil es den rBefehl fehlt ... :-(
1
Was ist mit: [[(if)2Q]si(condition)i(else)]x- Umhüllen eines Makros mit dem If-Teil eines anderen Makros, damit Sie 2Qsich aus dem Ganzen herausarbeiten können, bevor Sie den else-Teil erreichen. Wenn Sie also tun möchten, wenn 1 == 1, dann drucken Sie 1, sonst drucken Sie 2 , dann wäre es 1[[1P2Q]si1=i2P]x(ungetestet, da ich hier und jetzt keinen Zugriff auf dc habe. War mir auch sicher, dass ich diesen Trick in einer Antwort hier zuvor getan habe aber konnte es nicht finden)
daniero
Ja, ich habe nachgerechnet, mein Vorschlag ist kürzer. Mit dem gleichen Beispiel und der gleichen "Notation" und dem Entfernen von Leerzeichen beträgt der Unterschied [/*else*/]sE[[/*then*/]sE]sIlalb=IlExvs [[/*then*/2Q]sIlalb=I/*else*/]x- 6 Byte. Noch nicht getestet tho: P
daniero
1
Gute Arbeit, @daniero! Ich werde den Beitrag aktualisieren, wenn ich Zeit habe, oder du kannst es tun, wenn du willst.
Joe
5

Mit können Sie Eingaben speichern d

Mit d, das den ToS (Top of Stack) dupliziert, können Sie die Eingabe für die spätere Verwendung aus dem Weg räumen, während Sie sie weiterhin verwenden können.

Rɪᴋᴇʀ
quelle
@NoOneIsHere oh cool !!! Vielen Dank!
14.
5

Arrays

Obwohl sie Anfängern Kopfzerbrechen bereiten, dcbietet sie Arrays an. Sie arbeiten so:

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

Wie üblich hat das erste Element den Index 0. Arrays können nützlich sein, wenn Sie mit Sequenzen arbeiten, wie in der SUDSI-Sequenz , insbesondere in Kombination mit Zählern. Arrays können die Anzahl der erforderlichen Mischen reduzieren (und die Anzahl der Zähler und Vergleiche), wenn Sie ein bestimmtes Element auswählen möchten, ohne Ihre Umgebung zu zerstören. Wenn Sie beispielsweise einen Stapel Zahlen in ein Array verschieben möchten, können Sie eine rekursive Funktion schreiben, die z(Stapeltiefe) oder z 1-als Index verwendet, das Element speichert und prüft, ob z == 0sie sich selbst beenden soll.

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

Beachten Sie Folgendes:

  • Arrays sind Instanzen auf benannten Stapeln zugeordnet. Wenn Sie einen neuen Wert auf einen Stapel verschieben, dem ein Array zugeordnet ist, wird dieses Array ebenfalls "zurückgeschoben", und ein "neues" Array wird an seine Stelle gesetzt. Das alte Array kann erst verwendet werden, wenn der entsprechende Wert auf dem genannten Stapel ebenfalls verwendet werden kann (dh über dem Stapel). Dies ist ein kompliziertes Konzept, das sich mit einer guten Animation besser erklären lässt, was mir ein Rätsel ist.
  • Sie können Daten in einem benannten Array speichern, ohne einen Wert in das entsprechende benannte Register zu verschieben. In diesem Fall können Sie jedoch für den Rest der Sitzung nicht auf den Stapel bzw. das Register mit diesem Namen zugreifen. dcwird abstürzen.
  • Wenn Sie einen Wert von einem benannten Stack entfernen, gehen alle Werte im entsprechenden Array verloren - keine Warnungen, keine Sicherheitsmaßnahmen, nichts. Einfach weg (was auch nützlich sein kann).
Joe
quelle
Gute Arbeit mit den DC-Tipps!
22.
dcwurde möglicherweise kürzlich aktualisiert, und das Array-Verhalten hat sich in Bezug auf Abstürze möglicherweise geringfügig geändert. Kann im Moment auch nicht bestätigen, aber ich denke, etwas war anders, als ich es das letzte Mal unter Linux verwendet habe.
Joe
1
Wenn Sie versuchen, einen Index aus einem Array zu lesen, das nicht festgelegt wurde, erhalten Sie 0 und keinen Fehler. Was sehr nützlich sein kann, aber auch bedenkenswert ist, wenn Sie möglicherweise Nullen in Arrays einfügen ... Sie benötigen eine andere Methode, um zu testen, ob der Index berührt wurde.
Brhfl
5

0 bis zur n-ten Potenz anstelle von Bedingungen / Makros

Manchmal könnte man so etwas brauchen dreifach konditionalen:

A == B ? C : D;

Eine gute Möglichkeit, damit umzugehen, ist in der Antwort von @ Joe beschrieben . Wir können es jedoch besser machen:

0AB-^E*C+

wo E ist D - C.

Bei diesem Test wird die Gleichheit überprüft, indem 0 zur Potenz der Differenz der beiden Werte angehoben wird. Dies ergibt 1 wenn gleich und 0 sonst. Der Rest skaliert einfach die 1 oder 0 auf die Werte C oder D. Dies funktioniert, da dc0 0 = 1 und 0 n = 0 für n! = 1 ist.

Digitales Trauma
quelle
4

Manchmal muss eine Zahl aus dem Stapel entfernt werden. Eine Möglichkeit, dies zu tun, besteht darin, es einfach in eine nicht verwendete Variable einzufügen, d st. H. In einigen Situationen können Sie es jedoch an eine Reihe anderer Stellen einfügen, z. B. in die Eingabebasis, wenn Sie keine numerischen Eingaben mehr haben, oder in den Präzisionsspezifizierer, wenn Sie keine Operationen mehr ausführen müssen, bei denen die Genauigkeit einen Unterschied machen würde. Im ersteren Fall verwenden Sie i. Im letzteren Fall verwenden k.

Digitales Trauma
quelle
Wenn die numerische Ausgabe nicht wichtig ist, okann sie auch verwendet werden. Und wenn alle diese Dinge unwichtig sind, können sie als Speicher verwendet werden , als auch als bloße Verwerfungs - I/ K/ Oabrufen bzw., und speichert die Bytes über sa/ lausw. Gültige Werte AFAIK: i16.02; kjede nichtnegative ganze Zahl; ojede ganze Zahl größer als 1.
brhfl
4

Länge Berechnung: Z, X, undz

ZÖffnet den ToS und gibt die Anzahl der Stellen (dezimal) aus, wenn es sich um eine Zahl handelt, oder die Anzahl der Zeichen, wenn es sich um eine Zeichenfolge handelt. Dies kann nützlich sein, um die Länge eines Ergebnisses zu ermitteln (um die Ausgabe zu puffern) oder die Länge einer Zeichenfolge zu berechnen. Beachten Sie, dass für Zahlen Zdie kombinierte Länge des ganzzahligen Teils und des Bruchteils verschoben wird.

XKnallt den ToS und schiebt die Anzahl der Ziffern in den Bruchteil der Zahl. Wenn der ToS ein String war, 0wird er gedrückt.

Um die Anzahl der Ziffern im ganzzahligen Teil der Zahl zu finden, könnte man verwenden dZrX-. Wenn Sie die Genauigkeit nicht von der Standardeinstellung geändert haben k==0, ist using 1/Zkürzer, aber nehmen Sie an, dass Sie nach der Operation eine bestimmte Genauigkeit ungleich Null beibehalten müssen: Kr0k1/Zrkist eher ein Dorn im Auge.

zdrückt die Anzahl der Elemente auf dem Stapel. Einer meiner Lieblingsbefehle, es werden keine Werte angezeigt! Es könnte verwendet werden, um eine Folge von Zahlen zu erzeugen oder einen Zähler zu erhöhen. Durch zdwiederholtes Verwenden (beispielsweise am Anfang eines Makros) kann eine Berechnung für jede natürliche oder ganze Zahl in aufsteigender Reihenfolge durchgeführt werden.

Joe
quelle
Habe zdies und das schon einmal benutzt, aber ich
bin
4

Ziffern A, Fdie anstelle der Ziffern 10 bis 15 verwendet werden sollen. Sie müssen jedoch an verschiedenen Stellen effektiv als Ziffern der Basis 10 behandelt werden (vorausgesetzt, die Eingabebasis ist 10). Mit anderen Worten, FFwenn die Eingabebasis 10 nicht 255 darstellt, würde sie (15 * 10) + 15oder 165 darstellen.

In der Tat funktioniert dies für alle Ziffern 0zu Feinem der Eingabe - Basis 2zu 16. Wenn also die Eingabebasis 5 ist, dann 26Ewäre das (2 * 5^2) + (6 * 5) + 14oder 94.

Beachten Sie, dass dieses Verhalten für die unveränderten GNU-Quellen gilt. Wie @SophiaLechner jedoch betont, scheinen RedHat-basierte Distributionen bc-1.06-dc_ibase.patch zu verwenden, wodurch dieses Verhalten geändert wird, sodass Ziffern> = ibase ibase - 1unabhängig von ihrem tatsächlichen Wert als behandelt werden. Beachten Sie, dass der TIO dc anscheinend nicht über das Patch bc-1.06-dc_ibase.patch verfügt (obwohl Fedora 28 ¯_ (ツ) _ / ¯).

Digitales Trauma
quelle
Dies ist nicht ganz richtig - obwohl einzelne Ziffern über der Eingabebasis wie gewünscht interpretiert werden, werden ungültige Ziffern für die Basis als (Basis-1) interpretiert, wenn das Literal mehrere Ziffern oder sogar einen Dezimalpunkt enthält. Also in Eingangsbasis 10 FFdarstellt 99, ist in Eingangsbasis 5 26Egleich 244, dh Basis 10 74.
Sophia Lechner
@SophiaLechner Bist du sicher? tio.run/##S0n@/9/QIJ/L0CCTy82tgMs0k8vIzLXg/38A Welche dcVersion verwenden Sie? Ich verwende GNU dc 1.4.1 auf Ubuntu und GNU dc 1.3 auf MacOS
Digitale Trauma
Interessant. Ich verwende 1.3.95 auf Red Hat und hier ist Ihr Beispielprogramm: [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 ... kann den Codeblock im Kommentar nicht zum Laufen bringen. Der Punkt ist, dass FFpAusgaben 99in 1.3.95. Könnten Sie dies dann in Ihrer MacOS-Version überprüfen?
Sophia Lechner
1
Sichere Sache! Vielen Dank für die Recherche.
Sophia Lechner
2

Verwenden Sie beim Initialisieren eines Funktionsmakros (wir verwenden es F), das Sie sofort ausführen möchten, dsFxeher etwas wie als sFlFx. Dasselbe gilt für Variablen: dsaanstatt sala.

Wenn Sie zwischen dem Speichern und Laden noch andere Aufgaben erledigen müssen (z. B. sa[other stuff]la), prüfen Sie immer noch, ob die oben genannten Aufgaben durchführbar sind: Wenn Sie vor den anderen Vorgängen einen Wert auf dem Stapel belassen, befindet sich dieser am Ende wieder ganz oben dieser Operationen?

Joe
quelle
2

Gerade durch Zufall entdeckt. Ein weiterer Weg , eine Null zu erzeugen: _.

_ist ein Signal an DC, dass die folgenden Ziffern eine negative Zahl sind. Beispiel:

_3 # pushes -3

Was aber, wenn wir keine Zahl nachstellen?

_ # pushes 0...sometimes

Dies funktioniert, wenn das nächste nicht leere Zeichen nach dem Unterstrich keine Ziffer ist. Wenn eine Ziffer auch nach einer neuen Zeile folgt, wird sie als negatives Vorzeichen interpretiert.

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
Joe
quelle
1

Wenn der Inhalt des gesamten Stapels am Ende eines Programms gedruckt werden muss, kann eine rekursive Makroschleife verwendet werden, um dies zu erreichen. Es ist jedoch viel kürzer, einfach den fBefehl zu verwenden.

Digitales Trauma
quelle
1

dcLiest die Eingabe einer Zeile auf einmal. Wenn Sie mehrere Elemente einlesen müssen, müssen Sie entweder ?für jede zu lesende Zeile ein einzelnes Element oder eine umständliche Makroschleife verwenden. Wenn stattdessen alle Eingabeelemente in eine durch Leerzeichen getrennte Zeile gesetzt werden können, ?liest ein einzelnes Element alle Eingabeelemente und legt sie auf den Stapel.

Zum Beispiel in seq 10 | dc -e'?f', seqgibt ganze Zahlen von 1 bis 10, einen pro Zeile. Der ?liest nur den ersten, 1der ausgegeben wird, wenn fder gesamte Stapel gesichert wird. Doch in seq 10 | tr '\n' ' ' | dc -e'?f'der trmacht die Eingangs ganzen Zahlen alle Leerzeichen getrennt. In diesem Fall ?liest der alle Ganzzahlen auf einmal aus der Zeile und fgibt sie alle aus.

Digitales Trauma
quelle
1

Wenn ein Bediener von der Quelle ausgeschlossen ist, erstellen Sie einen neuen mit a

Was mir jetzt ein paar Mal geholfen hat, ist, die Verwendung eines bestimmten Operators zu vermeiden, indem der ASCII-Wert des Operators verschoben a, in einen String konvertiert und sin einem Register gespeichert wird, das später als Makro ausgeführt wird auf. Zum Beispiel muss ich eine Division durchführen, aber entweder ist mir die Verwendung des Zeichens untersagt oder ich versuche, sie zu vermeiden /. Ich kann stattdessen 47asdund dann in der Zukunft, wenn ich 16 durch 4 teilen muss 16 4 ldx.

  • Dies funktioniert nur für Einzelzeichenoperatoren (kann keine Zeichenfolge erstellen) und nicht für Befehle wie sdiese, die von etwas nachbearbeitet werden müssen.
  • Dies fügt einige Bytes hinzu und ist daher nur dann geeignet, wenn das Vermeiden des spezifischen Zeichens notwendig ist oder irgendwie einen Punktebonus gewährt.
brhfl
quelle
1

Leerzeichen vermeiden

Das Vermeiden von Leerzeichen ist mit einigen Herausforderungen verbunden und fällt in der Regel leicht dc. Abgesehen von Strings, die eine ganz bestimmte Zeit , dass Leerzeichen notwendig wird, wenn mehrere Zahlen in einer Reihe drücken: 1 2 3. Wenn dies vermieden werden muss:

  • Führen Sie ein leeres Makro zwischendurch : 1[]x2[]x3[]x.
  • Wenn Klammern vom Tisch sind, speichern Sie eine NOP eines Makros vor der Zeit: 35asnund führen Sie es zwischendurch : 1lnx2lnx3lnx.
brhfl
quelle
Sie können auch durch Kommas getrennte Zahlen verwenden, wenn Sie bereit sind, dc: ',' (054) unimplementedWarnungen zu akzeptieren.
Digital Trauma
Daran