Tipps zum Golfen in CJam

43

CJam ist eine GolfScript-inspirierte Stack-basierte Golfsprache, die von PPCG-Anwendern entwickelt wurde .

Also, in Anlehnung an andere sprachspezifische Tipps Fragen:

Welche allgemeinen Tipps haben Sie zum Golfen in CJam? Bitte posten Sie einen Tipp pro Antwort.

Martin Ender
quelle
4
Siehe auch Tipps zum Golfen in GolfScript ; Die Sprachen sind ähnlich genug, dass viele der Tricks in beide Richtungen angepasst werden können.
Ilmari Karonen
2
@IlmariKaronen Nachdem ich die Antworten in dieser Frage durchgesehen habe, würde ich sagen, dass nur etwa die Hälfte davon auf CJam zutrifft, da syntaktische oder logische Unterschiede in den Sprachen bestehen.
Optimierer

Antworten:

23

Korrigieren Sie das Modulo für negative Zahlen

Es ist oft ärgerlich, dass das Ergebnis der Modulo-Operation das gleiche Vorzeichen hat wie der erste Operand. ZB -5 3%gibt -2statt 1. Meistens möchten Sie Letzteres. Die naive Lösung besteht darin, Modulo anzuwenden, den Divisor einmal hinzuzufügen und Modulo erneut anzuwenden:

3%3+3%

Aber das ist lang und hässlich. Stattdessen können wir die Tatsache nutzen , dass Array - Indizierung immer modular aufgebaut ist und funktioniert richtig funktioniert mit negativen Indizes. Wir wandeln den Divisor einfach in einen Bereich um und greifen auf Folgendes zu:

3,=

Angewandt -5ergibt sich dies 1wie erwartet. Und es ist nur ein Byte länger als das eingebaute %!

Wenn der Modul eine Potenz von 2 ist, können Sie ein weiteres Byte mit bitweiser Arihmetik speichern (was auch viel schneller ist). Vergleichen Sie:

32,=
31&

Für den Sonderfall kann ein 65536 == 2^16anderes Byte gespeichert werden, indem das Umbruchverhalten des Zeichentyps ausgenutzt wird:

ci
Martin Ender
quelle
13

Verkettete Zeichenbereiche verschieben

  • Die Zeichenfolge mit allen Ziffern "0123456789"kann als geschrieben werden

    A,s
    
  • Die ASCII-Großbuchstaben ( A-Z) können als gedrückt werden

    '[,65>
    

    Dadurch wird die Zeichenfolge aller Zeichen bis zu Z generiert , und die ersten 65 (bis zu @ ) werden verworfen .

  • Alle ASCII-Buchstaben ( A-Za-z) können als gepusht werden

    '[,65>_el+
    

    Das funktioniert wie oben, erstellt dann eine Kopie, konvertiert in Kleinbuchstaben und hängt an.

    Aber es gibt einen kürzeren Weg!

    Der oft übersehene ^Operator (symmetrische Unterschiede für Listen) ermöglicht es, die gleichen Bereiche zu erstellen und dabei drei Bytes zu sparen:

    '[,_el^
    

    '[,Erstellt den Bereich aller ASCII-Zeichen bis Z , _elerstellt eine Kopie in Kleinbuchstaben und ^behält nur die Zeichen beider Zeichenfolgen bei, die in einer, jedoch nicht in beiden Zeichenfolgen enthalten sind.

    Da alle Buchstaben in der ersten Zeichenfolge Großbuchstaben, alle Buchstaben in der zweiten Kleinbuchstaben und alle Nichtbuchstaben in beiden Zeichenfolgen enthalten sind, ergibt sich die Zeichenfolge.

  • Das RFC-1642-Base64-Alphabet ( A-Za-z0-9+/) kann mithilfe der oben beschriebenen Methode übertragen und die Nicht-Buchstaben angehängt werden:

    '[,_el^A,s+"+/"+
    

    Eine ebenso kurze Art, diese Saite zu schieben, nutzt ausschließlich symmetrische Unterschiede:

    "+,/0:[a{A0":,:^
    

    Wie können wir die Zeichenfolge am Anfang finden?

    Alle eingesetzten Zeichenbereiche ( A-Z, a-z, 0-9, +, /) als Symmetriedifferenz des Bereich zu gedrückt werden , die an dem Null - Byte beginnen, nämlich 'A,'[,^, 'a,'{,^, '0,':,^, '+,',,^und '/,'0,^.

    Daher werden beim Ausführen :,:^von "A[a{):+,/0"die gewünschten Zeichen verschoben, jedoch nicht in der richtigen Reihenfolge.

    Wie finden wir die richtige Reihenfolge? Brute Force zur Rettung! Das Programm

    '[,_el^A,s+"+/"+:T;"0:A[a{+,/0"e!{:,:^T=}=
    

    Durchläuft alle möglichen Permutationen des Strings, wendet :,:^das Ergebnis an und vergleicht es mit der gewünschten Ausgabe ( Permalink ).

  • Das Radix-64-Alphabet, das z. B. von crypt ( .-9A-Za-z) verwendet wird, kann mit der obigen Methode generiert werden:

    ".:A[a{":,:^
    

    Dies ist die kürzeste mir bekannte Methode.

    Da alle Zeichen in der gewünschten Ausgabe in ASCII-Reihenfolge vorliegen, ist ein Durchlaufen der Permutationen nicht erforderlich.

  • Nicht alle verketteten Zeichenbereiche können mit in der gewünschten Reihenfolge verschoben werden :,:^.

    Zum Beispiel kann der Bereich 0-9A-Za-z;-?nicht verschoben werden, indem :,:^eine Permutation von ausgeführt wird "0:A[a{;@".

    Mithilfe des Codes können wir jedoch eine gedrehte Variante der gewünschten Zeichenfolge finden

    A,'[,_el^'@,59>]s2*:T;"0:A[a{;@"e!{:,:^T\#:I)}=Ip
    

    welches folgendes ausgibt ( permalink ):

    10
    0:@[a{A;
    

    Das bedeutet, dass

    "0:@[a{A;":,:^Am>
    

    hat den gleichen Effekt wie

    A,'[,_el^'@,59>]s
    

    die nur mit einem leeren Stapel ohne vorangestelltes a verwendet werden kann [.

Dennis
quelle
11

Vermeiden Sie {…} {…}?

Angenommen, Sie haben eine Ganzzahl auf dem Stapel. Wenn es ungerade ist, möchten Sie es mit 3 multiplizieren und 1 hinzufügen; ansonsten möchten Sie es durch 2 teilen.

Eine "normale" if / else-Anweisung würde folgendermaßen aussehen:

_2%{3*)}{2/}?

Die Verwendung von Blöcken ist jedoch normalerweise nicht der richtige Weg, da {}{}bereits vier Bytes hinzugefügt werden. ?kann auch verwendet werden, um eines von zwei Elementen auf dem Stapel auszuwählen:

_2%1$3*)@2/?

Dies ist ein Byte kürzer.


Block-? mit einer leeren if-Anweisung ist immer ein No-Go. Zum Beispiel,

{}{2/}?

ist zwei Bytes länger als

{2/}|

Wenn Sie stattdessen haben

{2/}{}?

und die Sache, die Sie überprüfen, ist nicht negative ganze Zahl, die Sie tun können

g)/

Die neuen {}&und {}|sind praktisch, aber manchmal problematisch, wenn Sie den Stapel nicht überladen können.

Im Fall von

_{…}{;}?

Sie können stattdessen eine temporäre Variable verwenden:

:T{T…}&
Dennis
quelle
1
!)/und g)/sind in den Beispielen kürzer.
Jimmy23013
11

Switch-Anweisungen

CJam hat keine switch-Anweisungen. Verschachtelte if-Anweisungen funktionieren genauso gut, sind aber {{}{}?}{}?bereits 12 Byte lang ...

Wenn wir die Bedingung in eine kleine, nicht negative Ganzzahl umwandeln können, können wir alle case-Anweisungen in eine durch Trennzeichen getrennte Zeichenfolge umwandeln und das entsprechende Ergebnis auswerten.

Wenn wir beispielsweise ausführen möchten, code0wenn die Ganzzahl des Stapels 0 ist , code1wenn es 1 ist und code2wenn es 2 ist , können wir entweder verwenden

_{({code2}{code1}?}{;code0}?

oder

[{code0}{code1}{code2}]=~

oder

"code0 code1 code2"S/=~

S/teilt den String in ["code0" "code1" "code2"], =extrahiert den entsprechenden Chunk und ~wertet den Code aus.

Klicken Sie hier , um switch-Anweisungen in Aktion zu sehen.

Schließlich können wir, wie von @ jimmy23013 und @ RetoKoradi vorgeschlagen, den Schalter in einigen Fällen noch weiter verkürzen. Sprich code0, code1und code2hat eine Länge L 0 , L 1 und L 2 jeweils.

Wenn L 0 = L 1 ≥ L 2

"code0code1code2"L/=~

kann stattdessen verwendet werden, wo List L 0 . Anstatt an einem Trennzeichen zu teilen, teilen Sie /die Zeichenfolge hier in gleichlange Abschnitte auf.

Wenn L 0 ≥ L 1 ≥ L 2 ≥ L 0 - 1 ist ,

"cccooodddeee012">3%~

kann stattdessen verwendet werden. >Entfernt 0, 1 oder 2 Elemente vom Anfang des Strings und 3%extrahiert jedes dritte Element (beginnend mit dem ersten).

Dennis
quelle
Hat das für das letzte Beispiel einen Vorteil gegenüber "code0code1code2"5/=~? Scheint mir viel unkomplizierter, und es ist die gleiche Länge.
Reto Koradi
@RetoKoradi Wenn alle Snippets die gleiche Länge haben, gibt es keinen Vorteil. Bei unterschiedlichen Längen kann Ihre Methode sowohl kürzer als auch länger sein als die Modulmethode.
Dennis
11

Gemeinsame Array- und String-Werte

Es gibt bestimmte kurze Arrays oder Strings, die hin und wieder auftauchen, z. B. um Grids zu initialisieren. Naiv können diese 4 oder mehr Bytes kosten, daher lohnt es sich, nach Operationen mit integrierten Werten zu suchen, die das gleiche Ergebnis liefern. Insbesondere die Basenumwandlung ist oft nützlich.

  • [0 1]kann geschrieben werden als 2,.
  • [1 0]kann geschrieben werden als YYb(dh 2 in binär).
  • [1 1]kann geschrieben werden als ZYb(dh 3 in binär).
  • Die Matrix [[0 1] [1 0]]kann geschrieben werden als 2e!.
  • [LL] kann geschrieben werden als SS/(Aufteilen eines einzelnen Leerzeichens durch Leerzeichen).
  • "\"\""kann geschrieben werden als L`.
  • "{}"kann geschrieben werden als {}s.

Letzteres kann auf Fälle ausgedehnt werden, in denen alle Klammertypen ein anderes Byte speichern sollen:

  • "[{<()>}]"kann geschrieben werden als {<()>}a`.
  • "()<>[]{}"kann geschrieben werden als {<()>}a`$.

Insbesondere der Trick der Basiskonvertierung kann nützlich sein, um einige unklare Fälle zu berücksichtigen, die hin und wieder auftauchen. ZB [3 2]wäre E4b(14 in Basis 4).

In noch selteneren Fällen kann der Faktorisierungsoperator mfhilfreich sein. ZB [2 7]ist Emf.

Bitte erweitern Sie diese Liste, wenn Sie auf andere Beispiele stoßen.

Martin Ender
quelle
10

Stapel leeren

Wenn Sie nur den gesamten Stapel löschen möchten, schließen Sie ihn in ein Array ein und fügen Sie ihn ein:

];

Etwas kniffliger ist es, wenn Sie viele Berechnungen durchgeführt haben, aber nur das oberste Stapelelement behalten und alles darunter verwerfen möchten. Der naive Ansatz wäre, das oberste Element in einer Variablen zu speichern, den Stapel zu löschen und die Variable zu verschieben. Es gibt jedoch eine viel kürzere Alternative: Wickeln Sie den Stapel in ein Array und extrahieren Sie das letzte Element:

]W=

(Danke an Optimizer, der mir das neulich gezeigt hat.)

Natürlich, wenn es nur zwei Elemente auf dem Stapel gibt, \;ist es kürzer.

Martin Ender
quelle
\;würde nur das Element unterhalb der TOS platzieren. Meinten Sie ;;?
CalculatorFeline
1
@CalculatorFeline In der zweiten Hälfte der Antwort geht es darum, alles außer den Nutzungsbedingungen zu löschen.
Martin Ender
9

e und Zehnerpotenzen

Wie in ach so vielen anderen Sprachen, können Sie schreiben , 1e3anstatt 1000in CJam.

Dies funktioniert für nicht ganzzahlige Basen und sogar für nicht ganzzahlige Exponenten. Zum Beispiel 1.23e2schiebt 123,0 und 1e.5schiebt 3,1622776601683795 (Quadratwurzel von 10 ).

Was nicht sofort offensichtlich ist, 1e3ist, dass es sich tatsächlich um zwei Token handelt:

  • 1schiebt die ganze Zahl 1 auf den Stapel.

  • e3multipliziert es mit 1000 .

Warum ist das so wichtig?

  • Sie können e<numeric literal>etwas aufrufen , das sich bereits auf dem Stapel befindet.

    2 3 + e3 e# Pushes 5000.
    
  • Sie können e<numeric literal>ein Array zuordnen.

    5 , :e3  e# Pushes [0 1000 2000 3000 4000].
    
Dennis
quelle
9

Euklidische Normen

Die einfache Methode zur Berechnung der euklidischen Norm eines Vektors, dh der Quadratwurzel aus der Summe der Quadrate seiner Elemente, ist

2f#:+mq

Es gibt jedoch einen viel kürzeren Weg.

mhDer Hypotenuse-Operator zieht zwei Ganzzahlen a und b aus dem Stapel und drückt sqrt (a 2 + b 2 ) .

Wenn wir einen Vektor x: = [x 1 … x n ] haben, erreicht n> 1 auf dem Stapel :mh(durch Hypotenuse reduzieren) Folgendes:

  • Zuerst werden x 1 und x 2 verschoben und mhausgeführt, wobei sqrt (x 1 2 + x 2 2 ) auf dem Stapel verbleibt .

  • Dann wird x 3 gedrückt und mherneut ausgeführt, wobei
    sqrt (sqrt (x 1 2 + x 2 2 ) 2 + x 3 2 ) = sqrt (x 1 2 + x 2 2 + x 3 2 ) auf dem Stapel verbleibt.

  • Nachdem x n verarbeitet wurde, verbleibt sqrt (x 1 2 +… x n 2 ) , die euklidische Norm von x .

Wenn n = 1 und x 1 <0 ist , führt der obige Code zu einem falschen Ergebnis. :mhzfunktioniert bedingungslos. (Danke an @ MartinBüttner für den Hinweis.)

Ich habe diesen Trick zum ersten Mal in dieser Antwort verwendet .

Dennis
quelle
2
Dies hat natürlich Auswirkungen auf die numerische Analyse Ihres Programms ...
Peter Taylor
8

Konvertieren Sie von der Basis n mit einer Liste von Zahlen, die größer als n sind

CJam konvertiert eine Liste in eine Zahl mit der folgenden Formel: A 0 * n l + A 1 * n l-1 + A 2 * n l-2 + A l * N 0 (mit nicht - negativ n). nist die Basis und list die Listenlänge. Dies bedeutet, dass A i eine beliebige ganze Zahl sein kann, die nicht im Bereich von liegen muss [0,n).

Einige Beispiele:

  • 0bextrahiert das letzte Element und wandelt es in eine Ganzzahl um. Funktioniert wie W=iund speichert ein Byte, wenn es keine Ganzzahl war. Alles andere in der Liste muss jedoch auch in eine Ganzzahl umgewandelt werden können.
  • 1bGibt die Summe zurück. Funktioniert wie :i:+und speichert zwei Bytes, wenn es sich nicht um ganze Zahlen handelt. Es funktioniert auch mit leeren Listen, wenn :+dies nicht der Fall ist.
  • [i{_1&9 32?_@\m2/}16*;]W%:ckonvertiert ein Zeichen in eine Folge von Zeilenenden und Tabulatoren, die mit zurückkonvertiert werden können 2bc. Die Codierungsfunktion ist jedoch nicht einfach in einem Code-Golf-Programm zu spielen. Aber das braucht man normalerweise nicht.
  • Sie können den folgenden Code verwenden, um eine Zeichenfolge in Unicode-Zeichen zu konvertieren, die nicht in 16-Bit-Zeichen vorliegen und die mit zurückkonvertiert werden können 2A#b128b:c. (Erklärungen werden später hinzugefügt. Oder ich schreibe später eine neue Version.)

    128b2A#b         " Convert to base 1024. ";
    W%2/)W%\:+       " Convert to two base 1024 digit groups. ";
    [0X@
    {
      _54+
      @I+_Am>@\-
      _Am<@+ 0@-@1^
    }fI
    ]);)
    @\+[~+]2A#b_2G#<!{2A#b}*
    \W%+:c
    

Die ähnliche Methode funktioniert mit jeder Menge von nGanzzahlen mit unterschiedlichen Werten mod n, wenn Sie eine Möglichkeit finden, die höchstwertige Ziffer zu entfernen.

jimmy23013
quelle
8

Verwenden $ als ternäres if

Wenn es Ihnen nichts ausmacht, Speicherplatz zu verlieren, dh nicht verwendete Elemente auf dem Stapel zu belassen, den Sie später löschen ];, $kann der Kopieroperator ein praktischer Ersatz für den ternären Operator sein? .

? Funktioniert gut, wenn Sie es schaffen, die Bedingung zu berechnen, bevor Sie die beiden Elemente zur Auswahl bringen. Meistens hängt die Bedingung jedoch von diesen Elementen ab, und es ist viel natürlicher, wenn sie darüber liegt.

Wenn Sie A B Cauf dem Stapel haben, können Sie ausführen

!$

Anstatt von

\@?

zu kopieren, Bwenn Ces wahr und Aanders ist.

Wenn Ces sich um einen tatsächlichen Booleschen Wert ( 0oder 1) handelt, können Sie ihn ausführen

$

Anstatt von

@@?

zu kopieren, Awenn Ces wahr und Banders ist.

Dennis
quelle
Im Nachhinein ist dies ein ziemlich offensichtlicher Trick, aber ich hatte noch nie daran gedacht. Ich habe es zum ersten Mal in dieser Antwort verwendet .
Dennis
7

Karte für verschachtelte Listen

Angenommen, Sie haben eine verschachtelte Liste wie eine Matrix:

[[0 1 2][3 4 5][6 7 8]]

Oder eine Reihe von Zeichenfolgen:

["foo""bar"]

Und Sie möchten einen Block auf die verschachtelte Ebene abbilden (dh auf jede Zahl oder jedes Zeichen anwenden). Die naive Lösung ist verschachtelt %:

{{...}%}%

Sie können jedoch den inneren Block tatsächlich auf den Stapel schieben und dann verwenden f%. fIst "map with additional parameter", so wird es %auf die äußere Liste abgebildet, wobei der Block als zweiter Parameter verwendet wird:

{...}f%

Spart zwei Bytes.

Ein weiterer guter Trick for (i=0; i<5; ++i) for (j=0; j<5; ++j) {...}ist

5,_f{f{...}}

Das äußere fwird auf den ersten Bereich abgebildet und liefert den zweiten Bereich als zusätzlichen Parameter. Aber jetzt, wenn Sie es ferneut verwenden, ist nur das oberste Stapelelement ein Array. Sie fordnen den inneren Block diesem zu und geben die äußere "Iterationsvariable" als zusätzlichen Parameter an. Dies bedeutet, dass der innere Block mit iund läuftj auf dem Stapel ausgeführt wird.

Dies hat die gleiche Anzahl von Zeichen wie nur das Abbilden eines Blocks auf ein kartesisches Produkt (obwohl das letztere kürzer wird, wenn Sie die Paare als Arrays benötigen):

5,_m*{~...}%

Der Unterschied besteht darin, dass diese Version eine einzige Reihe von Ergebnissen für alle Paare liefert, während die doppeltef liefert eine verschachtelte Liste liefert, die nützlich sein kann, wenn Sie die Ergebnisse in einem Raster speichern möchten, wobei die Iteratorvariablen die Koordinaten sind.

Vielen Dank an Dennis, der mir diesen Trick gezeigt hat.

0.6.4 Update

fund :sind jetzt immens verbessert worden, indem jeder andere Operator, einschließlich sich selbst, übernommen wurde. So können Sie jetzt noch mehr Bytes einsparen. Das Zuordnen eines Operators zu einer verschachtelten Liste wurde jetzt noch kürzer:

{:x}%
{x}f%
::x

Dies hilft jedoch nicht wirklich beim Zuordnen von Blöcken zu verschachtelten Listen.

Das Anwenden von Blöcken oder Operatoren auf das kartesische Produkt wurde jetzt auch kürzer, sowohl für Blöcke als auch für Operatoren:

5,_f{f{...}}
5,_ff{...}

5,_f{fx}
5,_ffx

Schön ist, dass Sie diese nun verschachteln können. So können Sie einen Operator genauso einfach auf die dritte Ebene einer Liste anwenden:

:::x

Oder ein Block mit einigen Tricks:

{...}ff%
Martin Ender
quelle
Tolles Update. Aber es gibt immer noch keine f~...
Jimmy23013
@ user23013 ferwartet einen binären Operator, ~ist unär; hast du vielleicht wollen :~? Wir können dies auch im Chat
aditsu
Vermisse ich etwas an diesem 0.6.4-Update? Ich erhalte immer noch Fehlermeldungen, wenn ich solche Tricks mache, wie Unhandled char after ':': :( link )
Runer112
2
@ Runer112 Funktioniert bei mir. Stellen Sie sicher, dass Sie richtig neu laden (dh nicht aus dem Cache). Abhängig von Ihrem Browser sollte Strg + F5 funktionieren.
Martin Ender
@ MartinBüttner Es wurde ja durch albernes Caching verursacht. Vielen Dank.
Runer112
7

Vektorisierte Operatoren für ASCII-Kunst

Bei vielen ASCII-Kunstherausforderungen ist es nützlich, zwei verschiedene Muster zu generieren, um sie später zu überlagern. Vektorisierte Operatoren können sehr hilfreich sein, um verschiedene Arten von Überlagerungen zu erzielen.

Eine nützliche Eigenschaft der Vektorisierung von Operatoren besteht darin, dass der Operator für jedes Element der kürzeren Zeichenfolge / des kürzeren Arrays nur einmal ausgeführt wird, während die Elemente der größeren Zeichenfolge, die keine Gegenstücke haben, unberührt bleiben.

  • .e<

    Der minimale Operator e< für Paare von Zeichenfolgen, Zeichen, Arrays und ganzen Zahlen. es wirft zwei Gegenstände vom Stapel und drückt den unteren auf den Rücken.

    Da ein Leerzeichen einen niedrigeren Codepunkt als alle anderen druckbaren ASCII-Zeichen hat, .e<können Sie Teile eines generierten Musters "löschen":

    "\/\/\/\/\/" "    " .e<
    
    e# This pushes "    \/\/\/".
    

    Ein vollständiges Beispiel finden Sie in meiner Antwort auf " Me Want Honeycomb" .

  • .e>

    Der maximale Operator e>arbeitet als minimaler Operator, mit dem entgegengesetzten Ergebnis.

    Auch hier kann aufgrund des niedrigen Codepunkts des Leerzeichens .e>ein Muster druckbarer Zeichen in einen Leerzeichenblock eingefügt werden:

    [[" " " " " " " "] [" " " " " " " "]][["+" "" "-" ""]["" "*" "" "/"]] ..e>
    
    e# This pushes [["+" " " "-" " "] [" " "*" " " "/"]].
    

    Ein vollständiges Beispiel finden Sie in meiner Antwort auf Seven Slash Display .

  • .e&

    Der logische AND-Operator e&drückt das linke Argument, wenn es falsch ist, und das rechte Argument, wenn es falsch ist.

    Wenn keines der Muster falsche Elemente enthält, kann dies verwendet werden, um bedingungslos ein Muster über ein anderes zu legen:

    "################" " * * * *" .e&
    
    e# This pushes " * * * *########".
    

    Ein vollständiges Beispiel finden Sie in meiner Antwort auf " Drucken der amerikanischen Flagge". .

  • .e|

    Der logische ODER-Operator e|kann wie oben beschrieben mit umgekehrter Argumentreihenfolge verwendet werden:

    " * * * *" "################" .e|
    
    e# This pushes " * * * *########".
    
Dennis
quelle
6

Verwenden Sie &diese Option , um zu überprüfen, ob sich ein Element in einer Liste befindet

Zum

1 [1 2 3] #W>
1 [1 2 3] #)

Sie können verwenden

1 [1 2 3] &,
1 [1 2 3] &

stattdessen wird 0/1 und truthy / falsey zurückgegeben.

jimmy23013
quelle
6

z und nicht rechteckige Arrays

Der zip-Operator ztransponiert die Zeilen und Spalten eines zweidimensionalen 1- Arrays A , dessen Elemente auch iterabel sein können.

Bei nicht rechteckigen Arrays konvertiert CJam im Gegensatz zu den integrierten zipFunktionen in Python (schneidet die Zeilen auf die gleiche Länge ab) oder Ruby ( nilfüllt die Zeilen mit Pads auf ) einfach die Spalten des Arrays in Zeilen, wobei die Längen und ignoriert werden Lücken.

Zum Beispiel das Array komprimieren

[
  [1]
  [2 4]
  [3 5 6]
]

entspricht dem Komprimieren des Arrays

[
  [1 4 6]
  [2 5]
  [3]
]

oder das Array

[
  [1]
  [2 4 6]
  [3 5]
]

wie alle drei Aktionen drücken

[
  [1 2 3]
  [4 5]
  [6]
]

auf dem Stapel.

Dies bedeutet zwar, dass zes sich nicht um eine Involution handelt (was gelegentlich nützlich wäre), es gibt jedoch einige Anwendungen.

Zum Beispiel:

  • Wir können die Spalten eines Arrays nach oben ausrichten (dh das erste Array in das zweite umwandeln), indem wir zweimal komprimieren:

    zz
    
  • Kleinere Modifikationen des obigen Verfahrens können für ähnliche Probleme verwendet werden.

    Um zum Beispiel die Spalten eines Arrays nach unten auszurichten (dh das zweite Array in das erste zu verwandeln), können wir zweimal in umgekehrter Reihenfolge zippen:

    W%zzW%
    
  • Bei einem gegebenen Array von Zeichenfolgen können wir die Länge der längsten Zeichenfolge folgendermaßen berechnen:

    :,:e>
    

    Durch Komprimieren und Berechnen der Anzahl der Zeilen des Ergebnisses können jedoch drei Bytes eingespart werden:

    z,
    

1 Wenn eine der "Zeilen" von A nicht iterierbar ist, werden zsie als Singletons behandelt, sodass das Komprimieren für beliebige Arrays funktioniert.

Dennis
quelle
1
Nur eine andere Art, dasselbe zu visualisieren, aber für mich ist das Verhalten viel logischer, wenn ich mir vorstelle, wie ich zSpalten in Zeilen konvertiere, während leere Werte übersprungen werden. Im Beispiel ist die erste Spalte in der Eingabe 1, 2, 3, die zweite Spalte ist 4, 5 (leere Position wird übersprungen) und die dritte Spalte ist 6. Dies sind dann die Zeilen des Ergebnisses.
Reto Koradi
@RetoKoradi Das ist ein viel besserer Weg, um es zu beschreiben.
Dennis
6

Ausnahmen

Alle Ausnahmen sind in CJam fatal. Da die Ausgabe in STDERR standardmäßig ignoriert wird , können wir dies zu unserem Vorteil nutzen.

Alle Bediener in CJam arbeiten, indem sie null oder mehr Elemente aus dem Stapel entfernen, eine Aufgabe ausführen und null oder mehr Elemente auf den Stapel verschieben. Während die Aufgabe ausgeführt wird, treten Ausnahmen auf, sodass die Elemente weiterhin angezeigt werden, aber nichts zurückgegeben wird.

Hier sind einige Anwendungsfälle:

  • Einen kleinen Stapel räumen

    Zum Löschen eines Stapels, der zwei Elemente enthält, @kann verwendet werden.@versucht, drei Stapelelemente einzufügen, schlägt jedoch nach dem Einfügen des zweiten fehl.

    Jeder andere Operator, der drei Elemente einblendet, hat denselben Zweck.

    Sehen sie in Aktion hier .

  • Entfernen von zwei oder drei Elementen vom Stapel

    Jeder Operator, der für diese bestimmten Elemente nicht implementiert ist, kann verwendet werden, um zwei oder drei Elemente unmittelbar vor dem Beenden aus dem Stapel zu entfernen.

    So platzieren Sie zwei Elemente: b funktioniert, wenn eines davon ein Zeichen oder keines eine Ganzzahl ist.

    Das Einfügen von drei Elementen tfunktioniert, wenn keines der beiden untersten Elemente eine Iteration ist, das unterste Element leer ist oder keines der Elemente eine Ganzzahl ist.

  • Verlassen einer Schleife

    Gelegentlich müssen wir eine Schleife verlassen, wenn eine Ganzzahl Null wird oder eine Zeichenfolge zu kurz wird. Anstatt auf diese Bedingungen zu testen, können wir das Programm einfach seinen natürlichen Verlauf lassen, wenn die beteiligten Operationen für Null, die leere Zeichenfolge oder Singletons fehlschlagen.

    Ein Beispiel zum Rechnen finden Sie hier .

    Ein Beispiel für Zeichenfolgen finden Sie hier .

  • Bedingte Ausführung

    Wenn der Quellcode für bestimmte Eingabetypen nicht ausgeführt werden soll, können wir manchmal einen Operator verwenden, bei dem diese Art von Eingabe fehlschlägt.

    Zum Beispiel iwird für Strings fehlschlagen , die auf eine ganze Zahl bewerten nicht und ewwird für Strings der Länge 0 oder 1 scheitern.

    Sehen sie in Aktion hier .

Dennis
quelle
5

Max / Min aus einem Array

Hier ist eine für den Anfang!

Wenn Sie die maximale oder minimale Anzahl eines Arrays ermitteln müssen, besteht die einfachste und kleinste Möglichkeit darin, das Array zu sortieren und dann das erste oder letzte Element herauszunehmen.

Also, wenn das Array variabel ist A

A$W=

ist das Maximum und

A$0=

ist das Minimum.

Beides gleichzeitig zu bekommen ist auch möglich

A$)\0=

Dies mag nach dem Lesen offensichtlich erscheinen, aber jeder erste Versuch tendiert dazu, das Array zu verwenden e<oder es zu e>durchlaufen, was so aussieht

A{e<}*

Das ist 2 Bytes länger und noch länger, wenn Sie sowohl max als auch min wollen.

Optimierer
quelle
Wenn Sie nichts dagegen haben, dass der Rest des Arrays auf dem Stack verbleibt, können Sie natürlich auch (und )anstelle von 0=und verwenden W=.
Martin Ender
Jetzt gibt es :e<und:e>
aditsu
@Aditsu Allerdings sind sie nicht kürzer als die Spitze oben.
Optimierer
5

Verwenden Sie einen Zeitstempel für große Zahlen

Wenn Sie eine sehr große, aber willkürliche Zahl benötigen, verwenden Sie normalerweise entweder die wissenschaftliche Notation 9e9oder erhöhen eine der großen eingebauten Variablen auf eine ähnliche Potenz wie KK#. Wenn es Ihnen jedoch egal ist, wie hoch die tatsächliche Zahl ist, und diese nicht durchgehend gleich sein muss (z. B. als Obergrenze für eine Zufallszahl), können Sie dies in zwei Bytes tun

es

stattdessen. Dies gibt den aktuellen Zeitstempel in Millisekunden an und liegt in der Größenordnung von 10 bis 12

Martin Ender
quelle
3
Beachten Sie auch, dass Sie verwenden können, wenn Sie eine große willkürliche Zahl möchten und eine positive Zahl zusammen verwerfen möchten e9.
Jimmy23013
5

Überprüfen Sie, ob zwei Zeichenfolgen / Arrays nicht gleich sind

Manchmal möchten Sie einen Wahrheitswert, wenn zwei Zeichenfolgen oder Arrays nicht gleich sind, und einen falschen Wert, wenn dies der Fall ist. Die naheliegende Lösung sind zwei Bytes:

=!

Auf Gleichheit prüfen und Ergebnis invertieren. Unter bestimmten Bedingungen können Sie jedoch verwenden

#

Wenn #es auf zwei Arrays angewendet wird, sucht es tatsächlich nach dem zweiten Array als Unterarray des ersten Arrays (und gibt den Index an, in dem das Unterarray beginnt). Wenn also die beiden Arrays gleich sind, wird das Subarray gleich zu Beginn gefunden und gibt 0, was falsch ist. Aber wenn das zweite Array nicht gefunden werden kann, wird es geben, -1was wahr ist.

Der Grund, warum wir eine zusätzliche Bedingung für die beiden Arrays benötigen, ist, dass dies auch einen falschen Wert ergibt, wenn das zweite Array ein nicht triviales Präfix des ersten ist, zB:

"abc""ab"#

gibt, 0obwohl die Saiten nicht gleich sind. Die einfachste Bedingung, die diesen Fall ausschließt, ist, wenn Sie wissen, dass beide Arrays die gleiche Länge haben. In diesem Fall wissen Sie, dass beide Arrays gleich sind, wenn eines dem anderen vorangestellt ist. Unter bestimmten Umständen können jedoch auch schwächere Bedingungen ausreichend sein. Wenn Sie beispielsweise wissen, dass die Zeichenfolgen sortiert sind, ist ein Präfix immer die erste und nicht die zweite Zeichenfolge.

Martin Ender
quelle
5

c und 16-Bit-Ganzzahlen

Um vorzeichenlose 16-Bit-Ganzzahlen mit korrektem Zeilenumbruch zu addieren (oder zu subtrahieren), können Sie +65536%oder verwenden +2G#%.

Jedoch,

+ci

ist viel kürzer. Zeichen werden bei 65536 umbrochen , daher hat das Umsetzen auf Character ( c) und dann auf Long ( i) einen ähnlichen Effekt wie 65536%, mit dem zusätzlichen Vorteil, dass das Ergebnis nicht negativ ist.

Mit demselben Trick kann 65535 gedrückt werden :

Wci
Dennis
quelle
4

Power setzt

Angenommen, Sie haben ein Array und möchten ein Array mit allen möglichen Teilmengen dieses Arrays. Der Trick besteht darin, mit einem leeren Array zu beginnen und dann für jedes Element die bereits vorhandenen Teilmengen zu duplizieren und ihnen das neue Element hinzuzufügen (wobei das vorherige Ergebnis beibehalten wird, in dem das Element nicht hinzugefügt wurde ). Beachten Sie, dass Sie den Stack mit dem Basisfall initialisieren müssen, dh einem Array, das nur ein leeres Array enthält: Das könnte so aussehen:

[1 2 3 4 5]La\{1$f++}/

Das Schöne daran ist, dass Sie sofort eine Berechnung für die Teilmenge ausführen können, möglicherweise ohne zusätzliche Zeichen. Angenommen, Sie möchten die Produkte aller Untergruppen. In diesem Fall ist der Basisfall ein Array, das Folgendes enthält 1: Bei jedem Schritt nehmen Sie die vorherige Liste möglicher Produkte, duplizieren sie und multiplizieren alles im Duplikat mit dem neuen Element:

[1 2 3 4 5]1a\{1$f*+}/
Martin Ender
quelle
4

Überprüfen Sie, ob die Elemente in einer Liste alle gleich sind

Ich finde das auch erwähnenswert. Verwenden:

)-

Gibt true zurück, wenn nicht alle gleich sind, oder leere Liste, wenn alle gleich sind. Fehler, wenn die Liste leer ist.

Falls das extrahierte Element selbst ein Array (oder eine Zeichenfolge) sein könnte:

)a-

Verwenden Sie !oder !!, um boolesche Werte abzurufen. Falls es sich bei dem extrahierten Element möglicherweise um ein Array handelt und es höchstens zwei verschiedene Elemente gibt und Sie möchten, dass es 1 ist, wenn nicht alle gleich sind, ist dies kürzer:

_|,(
jimmy23013
quelle
4

0= für Streicher

Um das erste Element eines Arrays abzurufen, müssen Sie verwenden 0=(oder (, wenn Sie nichts dagegen haben, den Rest des Arrays auf dem Stapel zu belassen).

Wenn es sich bei diesem Array jedoch um eine Zeichenfolge handelt, ist das Umwandeln in Zeichen ausreichend.

Beispiel

"xyz"c e# Pushes 'x.
Dennis
quelle
Ich verstehe nicht, warum CJam nicht einfach cdas erste Element eines Arrays extrahieren lässt , was nützlicher und konsistenter wäre.
Esolanging Fruit
4

Drehen eines Arrays (oder des Stapels) um eine Einheit nach links

CJam hat den Operator " Links drehen"m< , den Sie normalerweise verwenden sollten, um ein Array um eine beliebige Anzahl von Einheiten nach links zu drehen.

In einigen Fällen können Sie auch (+Folgendes verschieben und anhängen:

[1 2 3]       (+ e# Pushes [2 3 1].
[[1] [2] [3]] (+ e# Pushes [[2] [3] 1].

Das zweite Beispiel hat nicht funktioniert, da das erste Element des Arrays ebenfalls iterierbar ist, also +verkettet statt angehängt.

Wenn Sie das gedrehte Array auf dem Stapel ablegen möchten, können Sie auch :\bedingungslos Folgendes verwenden (durch Austauschen reduzieren):

[1 2 3]       :\ e# Pushes 2 3 1.
[[1] [2] [3]] :\ e# Pushes [2] [3] [1].

Solange Sie keine Öffnung haben [, können Sie mit diesem Trick auch den gesamten Stapel drehen, dh das unterste Stapelelement nach oben bringen:

]:\
Dennis
quelle
3

Liste drucken und Stapel leeren

Nehmen wir an, Ihr Stapel enthält eine Liste von Zeichenfolgen / Zahlen / etc. oben und einige andere zusätzliche Gegenstände darunter. dh

123 "waste" ["a" "b" "rty" "print" "me" "please"]

Jetzt möchten Sie nur die letzte Liste drucken, und das tun Sie auch

S*]W=

welche Ausgänge

a b rty print me please

Das scheint wirklich klug zu sein, wenn wir den Trick zum Löschen des Stapels verwenden und nur die Liste drucken, die mit Leerzeichen verbunden ist (was manchmal nicht die gewünschte Methode zum Drucken einer Liste ist).

Dies kann weiter golfen werden!

p];

Das sind 2 Bytes weniger !

und wenn Sie nur einen anderen Gegenstand als die Liste auf dem Stapel haben, ist er sogar noch kürzer!

p;

Die Schönheit von p ist, dass es das oberste Element aus dem Stapel entfernt, es stringifiziert (und am Ende eine neue Zeile hinzufügt) und sofort auf STDOUT druckt, ohne auf die Vervollständigung des Codes zu warten.

Der obige Code wird also ausgegeben

["a" "b" "rty" "print" "me" "please"]

Das ist die exakte Darstellung einer Liste, als sie im Stapel war!

Optimierer
quelle
3

Kartesische Produkte oder alle möglichen Kombinationen von zwei oder mehr Sätzen

CJam hat einen eingebauten kartesischen Produktrechner, m*der die beiden obersten Arraylisten / Strings auf einen Stapel legt und daraus alle möglichen Paare erstellt. Zum Beispiel

[1 2 3 4]"abc"m*

Blätter

[[1 'a] [1 'b] [1 'c] [2 'a] [2 'b] [2 'c] [3 'a] [3 'b] [3 'c] [4 'a] [4 'b] [4 'c]]

als der Stapel

Aber was ist, wenn Sie alle möglichen Kombinationen aus mehr als 2 Listen / Zeichenfolgen möchten. Du benutzt m*das oft? Zum Beispiel

[1 2 3 4][5 6]"abc"m*m*

Lässt das Folgende auf dem Stapel

[[1 [5 'a]] [1 [5 'b]] [1 [5 'c]] [1 [6 'a]] [1 [6 'b]] [1 [6 'c]] [2 [5 'a]] [2 [5 'b]] [2 [5 'c]] [2 [6 'a]] [2 [6 'b]] [2 [6 'c]] [3 [5 'a]] [3 [5 'b]] [3 [5 'c]] [3 [6 'a]] [3 [6 'b]] [3 [6 'c]] [4 [5 'a]] [4 [5 'b]] [4 [5 'c]] [4 [6 'a]] [4 [6 'b]] [4 [6 'c]]]

Beachten Sie, dass es sich bei den Produkten immer noch um Paare handelt, wobei einer der Artikel selbst ein Paar ist. Dies wird nicht erwartet und wir wollen abgeflachte Kombinationen.

Es gibt einen einfachen Weg, das zu tun. Wickeln Sie einfach jede Liste, die Sie für Ihr kartesisches Produkt möchten, in ein Array, erstellen Sie kartesische Produkte paarweise und reduzieren Sie sie jedes Mal:

[1 2 3 4][5 6]"abc"]{m*{(+}%}*

Diese Blätter

[['a 5 1] ['b 5 1] ['c 5 1] ['a 6 1] ['b 6 1] ['c 6 1] ['a 5 2] ['b 5 2] ['c 5 2] ['a 6 2] ['b 6 2] ['c 6 2] ['a 5 3] ['b 5 3] ['c 5 3] ['a 6 3] ['b 6 3] ['c 6 3] ['a 5 4] ['b 5 4] ['c 5 4] ['a 6 4] ['b 6 4] ['c 6 4]]

auf stapel.

Möchten Sie die Bestellung beibehalten? Tauschen Sie einfach das Element aus, bevor Sie es wieder zum Array hinzufügen. dh

{m*{(\+}%}*

Willst du nur Permutationen?

{m*{(+$}%_&}*

Willst du nur einzigartige Elemente in den Kombinationen?

{m*{(+_&}%}*

Das war's Leute. für jetzt .

Optimierer
quelle
1
Jetzt können Sie auch ]:m*:e_mit einer beliebigen Anzahl von Arrays arbeiten
am
3

Bearbeitung von Saiten

Wenn Sie mit einer komplexen Datenstruktur arbeiten und die darin enthaltenen Elemente einfach sind, kann es manchmal hilfreich sein, sie in Zeichenfolgen zu konvertieren.

Wenn Sie beispielsweise die ersten oder letzten Elemente in einem 2D-Array von Bits abrufen möchten und sich nicht für den zurückgegebenen Typ interessieren, sA< ein Byte von 0=A<oder gespeichert :+A<.

Wenn Sie einige Bits in der Eingabe ändern möchten, können Sie die Zeichenfolge vor der Auswertung ändern.

Oder wenn Sie diese Struktur haben und sie in eine einfache Liste konvertieren möchten:

[[[[[[[[[1]2]3]4]5]6]7]8]9]

Sie können es mit vielen Charakteren auf andere Weise tun:

[a{~)\}h;]W%

Bei Streichern kann es aber auch viel kürzer sein:

s:~

Es ist kürzer, auch wenn es Zahlen mit mehr als einer Ziffer haben kann:

[`La`-~]

Oder:

`']-~]

Wenn Sie kein anderes Array benötigen, das viele solcher Arrays enthält.

jimmy23013
quelle
Es ist e_jetzt
aditsu
@aditsu Siehe diese Antwort und Kommentar . Funktioniert manchmal snoch besser.
Jimmy23013
Klar, wenn Sie direkt mit einer Zeichenfolge arbeiten können, ist sie kürzer.
Aditsu
3

Verwenden N anstelle vonLa

In vielen Fällen benötigen Sie eine Initialisierung für ein Array, das ein leeres Array als einziges Element enthält La scheint unnötigerweise 1 Byte länger zu sein.

In vielen Fällen müssen Sie vor dem Drucken nach jedem Element eine neue Zeile einfügen, wie z. B. NooderN* . .

Wenn beides zutrifft, werden Sie manchmal feststellen, dass Sie das Array nur mit initialisieren können N , dessen einziges Element das Newline-Zeichen ist. Stellen Sie sicher, dass Sie den Elementen im Rest Ihres Codes nur Dinge voranstellen. Das erste, was Sie voranstellen müssen, ist immer ein Zeichen oder ein Array. Oder nur anhängen, wenn eine führende Zeile akzeptabel ist und diese dadurch kürzer wird.

Funktioniert manchmal Sauch, wenn Sie die Ausgabe durch Leerzeichen trennen müssen.

In seltenen Fällen muss das Anfangselement eine Zeichenfolge sein. Sie können jedoch weiterhin verwenden, Nawas möglicherweise kürzer ist, als die neue Zeile danach anzuhängen.

jimmy23013
quelle
2

Aufteilen bei einem oder mehreren Vorkommen

Angenommen, Sie haben eine Zeichenfolge "abbcdbbfghbdbb"und möchten sie aufteilenb

"abbcdbbfghbdbb"'b/

Dies lässt auf Stapel:

["a" "" "cd" "" "fgh" "d" "" ""]

Beachten Sie die leeren Zeichenfolgen? Die sind da, weil zwei bzusammen waren und nichts dazwischen war. Manchmal möchten Sie dies vermeiden. Sie können dies tun, indem Sie

"abbcdbbfghbdbb"'b/La-

oder leere Zeichenketten herausfiltern

"abbcdbbfghbdbb"'b/{},

das sind aber 3 extra bytes.

Ein etwas weniger bekannter Operator für diesen speziellen Anwendungsfall ist %. Neben tun mod und Karte und Splitting basierend auf der Anzahl ( "abcd"2%= "ac"), %kann auch aufgeteilt auf Strings / Arrays. Also für den obigen Anwendungsfall:

"abbcdbbfghbdbb"'b%

wird verlassen

["a" "cd" "fgh" "d"]

auf stapel.

Vielen Dank für @ user23013, dass Sie in einer meiner heutigen Antworten darauf hingewiesen haben.

Optimierer
quelle
Ich denke, das sollte "auch GolfScript lernen" heißen, was in der Dokumentation bessere Beispiele hat.
jimmy23013
@ user23013 aber wir sind uns nie sicher, was alles GS ähnelt und was nicht.
Optimierer
2

Verwenden Sie Fold / Reduce als Infix für jeden

Wir haben :xals Abkürzung für {x}%und oder {x}*(je nachdem ob xunär oder binär). Leider gibt es keinen äquivalenten Infix-Operator zum Kürzen {x}/. Aber sehr oft , wenn wir tun {x}/, xist eigentlich ein binärer Operator , der wiederholt das Element darunter liegenden auf dem Stapel modifiziert. Wenn dies der Fall ist und das Item kein Array ist, können wir ein Byte speichern, indem wir fold / reduction als foreach missbrauchen:

5 [1 2 3 4]{-}/  e# Gives -5
5 [1 2 3 4]+:-

Dies funktioniert, weil beim Falten immer das erste Element unberührt bleibt. Leider wird kein Byte gespeichert, wenn das geänderte Element ein Array ist, da das Hinzufügen es auspacken würde. Aber manchmal haben Sie Glück genug , dass Ihr Array enthält bereits das Element an der Front, in welchem Fall reduce sollte im Auge (statt manuellen Entfernen des Elements vor der Verwendung gehalten werden {}/auf dem Rest).

Martin Ender
quelle
2

drucken und drucken

CJam hat print Betreiber: o. Es funktioniert, aber der Stapel wird sofort gedruckt, nachdem der gesamte Code ausgeführt wurde. Sie können es stoppen, wenn Sie den Stapel am Ende des Programms löschen. Einfach am Ende setzen:

];

Zum Ausdrucken können Sie oNooder verwenden p(funktioniert wie `oNo).

Benutzername.ak
quelle
3
Es gibt einen größeren Unterschied zwischen ound p. pBeginnt mit der Konvertierung des zu druckenden Elements in eine eindeutige Zeichenfolgendarstellung. pist gleichbedeutend mit der Ausführung ​`oNo.
Dennis