Tipps zum Golfspielen in Postscript?

14

Als eine der weniger populären Sprachen ist es schwierig, Literatur über die Avantgarde des Postscript-Hackery zu finden. Welche Entdeckungen haben die Golfer hier gemacht, um das Stack-Modell (oder andere Merkmale) zu nutzen, um die inhärente Ausführlichkeit von Postscript zu überwinden?

Luser Droog
quelle
Einige externe Seiten wurden gefunden: sites.google.com/site/codegolfingtips/Postscript
luser droog

Antworten:

3

Embedded Decoder

Ein Postscript-Programm hat die einzigartige Fähigkeit, seinen eigenen Programmtext als Daten zu lesen. Dies wird normalerweise durch den verwendeten imageOperator empfängt , die einen Datenerfassungsprozedur als Eingabe, und dieses Verfahren häufig verwendet , currentfilegefolgt von readline, readstring, oder readhexstring. Aber eine andere Art und Weise zu sehen ist , imageist nur ein weiterer Looping - Operator, so dass jede Schleife kann Read-Ahead . Ein Beispiel ist der Zeilendrucker-Emulator aus dem Green Book.

Wenn Sie den tokenOperator verwenden, wird der Scanner für eine Datei oder eine Zeichenfolge aufgerufen, wobei ein durch eine Zahl oder ein Leerzeichen (oder etwas anderes) begrenzter Name abgerufen wird.

Ein einfacher PS-Interpreter in PS:

{currentfile token not {exit} if dup type /arraytype ne {exec} if }loop

Binäroperator-String-Decoder

Da ich scheinbar keine rohen Binärtoken für mich zum Laufen bringen kann (siehe andere Antwort), habe ich die Idee der "eingebetteten Dekodierung" genutzt, um den Binärtoken-Mechanismus auszunutzen, um Code in 8-Bit-Zeichenfolgen zu packen, und dann Manipulieren und analysieren Sie die Befehle aus der Zeichenfolge im laufenden Betrieb .

/.{
    <920>  % two-byte binary-encoded name template with 0x92 prefix
    dup 1 4 3 roll put  % insert number into string
    cvx exec  % and execute it
}def
/${
    //.   %the /. procedure body defined above
    73 .  %"forall" (by code number)
}def

Die .Prozedur entnimmt eine Zahl aus dem Stapel und fügt sie als zweites Byte in eine Zwei-Byte-Zeichenfolge ein, wobei das erste Byte das Präfix-Byte für ein binäres Token ist und einen ausführbaren Systemnamen angibt. Wir speichern ein Byte im Hexstring, indem wir die Regel des Scanners verwenden, dass eine ungerade Anzahl von Halbbytes im Hexstring mit einem zusätzlichen 0-Halbbyte aufgefüllt wird, sodass 3 Hex-Halbbytes einen 2-Byte-String ergeben. Die Zeichenfolge wird dann als ausführbar markiert und aufgerufen, execwodurch der Scanner aufgerufen wird, der gewünschte Name des ausführbaren Systems erzeugt wird, der Name geladen und der Operator ausgeführt wird. Das $tut dies für jedes Byte einer Zeichenfolge auf dem Stapel, mit der .Prozedur zweimal , einmal als die Schleife und dann den Looping Bediener auszuführen foralldurch Nummer.

Diese Prozeduren sehen kompakter wie folgt aus:

 /.{<920>dup 1 4 3 roll put cvx exec}def/${//. 73 .}def
%123457890123456789012345678901234567890123456789012345
%        1         2         3         4         5

Also, 55 Zeichen kaufen binäre Token-Zeichenfolgen. Oder für 6 (vielleicht 7, wenn Sie es mit einem Leerzeichen abschließen) Zeichen können Sie die G-Bibliothek laden, mit (G)runder .und $wie oben definiert (+ einige andere, um den Bereich der über ASCII erreichbaren Codes zu erweitern).

Weitere illustrierte in meinem Kreuzworträtsel Antwort .

Luser Droog
quelle
1
Wenn das Nachskript in verschlüsselter Form vorliegt, müssen Sie beim Vergleichen der Inhalte besonders vorsichtig sein. Insbesondere wenn Sie Operatoren betrachten, müssen Sie sie als Token analysieren und dann den Namen des Tokens mit dem Zielwert vergleichen.
AJMansfield
2

Verwenden Sie =anstelle von, wenn Sie eine grafische Ausgabe erstellen und die Konsolenausgabe keine Rolle spielt pop.

Thomas W.
quelle
2

Ersetzen Sie Hexstrings durch ASCII85

Wahrscheinlich alte Nachrichten, aber ich habe es gerade gelernt. :)

Sie können den Postscript-Interpreter interaktiv mit einem Codierungsfilter und Ausschneiden und Einfügen verwenden. Aber ich werde zeigen, wie dcman es "von Hand" macht.

Also, hier ist eine Hex-Zeichenfolge. Wir haben es in 4-Byte-Blöcke aufgeteilt.

95 20 6e d8   d0 59 49 35   50 74 ba c5   08 2d

Beim Starten von DC geben wir diese als 32-Bit-Big-Endian-Byte-Ordnungszahlen (ohne Vorzeichen) ein. Dann mod -off base-85 Stellen (es sollte 5 geben, bis Sie auf 0 kommen).

0> dc
16i
95206ED8
Ai
d85% n85 /
82
d85% n85 /
83
d85% n85 /
82
d85% n85 /
78
d85% n85 /
47
d85% n85 /
0                    

Das Auffüllen des letzten Blocks mit 00 00ergibt (dezimal), wobei die gleiche Anzahl von Bytes weggelassen wird, die wir aufgefüllt haben.

47 78 82 83 82   66 81 72 79 83   25 72 82 25 69  2 53 30 [2 53]

Fügen Sie 33 hinzu, um in den druckbaren Bereich von ASCII und Poof zu wechseln! ASCII85.

80 111 115 116 115 99 114 105 112 116 58 105 115 58 102 35 86 63,
die dekodiert: Postscript: ist: f # V? %%%Hoppla! sollte "Spaß" sagen! Ich habe es irgendwo vermasselt. :)

Wrap it in <~... ~>und Level-2 Postscript kann auf 8-Bit-Daten zugreifen, die billiger als Hex sind.

Luser Droog
quelle
2

Hier ist eine kurze Beschreibung: Binden Sie mehrere Definitionen ein [...>>begin, um das Schlüsselwort zu defentfernen (Nr. Ist [dasselbe wie <<).

 def def
[>>begin

Also denk dran: mehr alsdreizwei ... scharen sich zusammen ! ;)

Luser Droog
quelle
Sollte die Regel nicht "mehr als zwei" sein? Vergleichen Sie /a 1 def/b 2 def/c 3 defmit <</a 1/b 2/c 3>>begin. Wir brauchen mehr Platz für def.
Thomas W.
Beeindruckend. Daran hatte ich nicht gedacht. Ja, die Berechnung muss verbessert werden.
Luser Droog
Eigentlich sollte das sein[/a 1/b 2/c 3>>begin
Thomas W.
Gesicht-Handfläche. . . .
Luser Droog
1
Dies gilt möglicherweise nicht, wenn Sie etwas benennen, das auf einem selbstbegrenzenden Token endet. In /a{pop 2 mul}defoder kostet \b[2 3]defdas defnur 3 Zeichen, nicht 4.
AJMansfield
2

Während die meisten Postscript - Operatoren syntaktisch Identifikatoren sind (und damit platz- sein muss (oder otherwise-) begrenzt ist ), die Namen [, ], <<, und >>sind selbstbegrenze und Scanner werden sie erkennen , ohne Raum dazwischen. Aus dem gleichen Grund, Sie nicht auf diese Namen mit der üblichen beziehen /literalSyntax (. ZB /[zwei Token: ein leerer wörtliche Namen entspricht ()cvn cvlit, und die Namen der ausführbaren Datei [entspricht ([)cvn cvx exec).

Um diese Namen neu zu definieren, die nicht namentlich erwähnt werden können, können wir Zeichenfolgen verwenden, die implizit in Namen konvertiert werden, wenn sie als Schlüssel in einem Wörterbuch verwendet werden (praktisch!).

In diesem Beispiel wird der Missbrauch dieser Operatoren zum Ausführen von Arithmetik veranschaulicht.

%!
([)0 def
(])1 def
(<<){add}def
(>>){mul}def
 ]]<<]]]<<<<>> =
%1 1 add 1 1 1 add add mul = %prints 6

Auch <<und [(und mark) bedeuten alle dasselbe.


Mein eigener Postscript-Interpreter, xpost , stellt mit einigen Einschränkungen auch die rechte geschweifte Klammer zur Verfügung. Diskussion

Luser Droog
quelle
2
Auch /endet die bisherigen Token so dass Sie nicht einen Raum , bevor es brauchen.
Geoff Reedy
1

Vermeiden Sie wiederholte Verwendungen langer Bedienernamen

Wenn Sie bereits ein <<>>beginWörterbuch verwenden, entsteht ein konstanter Overhead von /?{}4 Zeichen pro Neudefinition. Ein Operator der Länge n, der N- mal wiederholt wird, ergibt eine Änderung der Zeichenanzahl von
(4 + n ) - ( N * ( n - 1)).

Wenn Sie diese Formel auf 0 setzen, erhalten Sie die Gleichung für den Break-Even- Punkt. Daraus können wir für jede Variable im Hinblick auf die andere lösen und
n = - ( N - 4) / (1 - N ) und
N = (4 + n ) / ( n - 1) ergeben.

Nein, wir können Fragen beantworten wie: "Bei wie vielen Verwendungszwecken von 'Drucken' lohnt es sich, abzukürzen?" n = 5, also N = 9/4. Nehmen Sie die Decke, da Sie Print nicht 1/4 Mal aufrufen können. Also, 3. 3 verwendet. Und in der Tat,

print print print
/P{print}p p p

(vorausgesetzt, Sie haben bereits den Aufwand für <<>>begindie Aktivierung der Definition bezahlt ).

Natürlich machen binäre Token diese Art von Moot und geben Ihnen die ersten 255 Namen aus der Systemnamentabelle als 2-Bytes: 0x92, 0x ??. Binäre Token sind ebenfalls selbstbegrenzend und erfordern weder vorher noch nachher Leerzeichen, da das High-Bit des ersten Bytes außerhalb des ASCII-Bereichs liegt.

Luser Droog
quelle
1

Binäre Token

Für das ultimative Zip-up eines PostScript-Programms besteht diese letzte Grenze aus binären Token, mit denen Sie lange Bedienernamen vollständig entfernen können, ohne ein ASCII-Clean-Programm mehr zu haben.

Beginnen Sie also mit einem komprimierten Block mit Postscript-Code

[/T[70{R 0 rlineto}48{}49{}43{A rotate}45{A neg rotate}91{currentdict
end[/.[currentpoint matrix currentmatrix]cvx>>begin begin}93{. setmatrix
moveto currentdict end end begin}>>/S{dup B eq{T begin exch{load exec}forall
end}{exch{load exch 1 add S}forall}ifelse 1 sub }>>begin moveto 0 S stroke

Wir schlagen alle Namen auf der Rückseite des PLRM nach (Anhang F, S. 795-797).

appearance
in
vim    dec  meaning

<92>   146  'executable system name' binary token prefix
^A     1    add
^M     13   begin
^^     30   currentdict
'      39   currentmatrix
(      40   currentpoint
2      50   cvx
8      56   dup
9      57   end
=      61   eq  !)
>      62   exch
?      63   exec
I      73   forall
U      85   ifelse
d      100  load
h      104  matrix
k      107  moveto
n      110  neg
<85>   133  rlineto
<88>   136  rotate
§      167 stroke
©      169 sub

Und geben Sie sie dann mit einem 146(Dezimal-) Byte voran ein. vim hilfe zur eingabe beliebiger bytes

In vim kann die komprimierte Datei dann direkt eingegeben werden.

[/ T [70 {R 0 ^V146 ^V133} 48 {} 49 {} 43 {A ^V146 ^V136} 45 {A ^V146 ^V110 ^V146 ^V136} 91 { ^V146 ^V30 ^V146 ^V57 [/. [ ^V146 ^V40 ^V146 ^V104 ^V146 ^V39] ^V146 ^V50 >> ^V146 ^V13 ^V146 ^V13} 93 {. ^V146 ^V156 ^V146 ^V107 ^V146 ^V30 ^V146 ^V57 ^V146 ^V57 ^V146 ^V13} >> / S { ^V146 ^V56 B ^V146 ^V61 {T ^V146 ^V13 ^V146 ^V62 { ^V146 ^V100 ^V146 ^V63}^V146 ^V73 ^V146 ^V57} { ^V146 ^V62 { ^V146 ^V100 ^V146 ^V62

... Sie müssen hier ein Leerzeichen eingeben, um die ^V-62 zu beenden und die 1 zu starten, aber Sie können sie später sichern und löschen ...

1 ^V146 ^V1S} ^V146 ^V73} ^V146 ^V85

... müssen hier ein Leerzeichen eingeben, um die ^V-85 zu beenden und die 1 zu starten, aber Sie können sie später sichern und löschen ...

1 ^V146 ^V169} >> ^V146 ^V13 ^V146 ^V107

... Die dritte Ziffer des dreistelligen Codes beendet die Byte-Eingabe, so dass das Folgende 0hier normal ist, bequemerweise ...

0 S ^V146 ^V167

Welches wird auf dem Bildschirm so aussehen (in vim):

[/T[70{R 0<92><85>}48{}49{}43{A<92><88>}45{A<92>n<92><88>}
91{<92>^^<92>9[/.[<92>(<92>h<92>']<92>2>>
<92>^M<92>^M}93{.<92><9c><92>k<92>^^<92>9<92>9<92>^M}
>>/S{<92>8B<92>={T<92>^M<92>>{<92>d<92>?}<92>I<92>9}{<92>>
{<92>d<92>>1<92>^AS}<92>I}<92>U1<92>©}>><92>^M
<92>k0 S<92>§

Diese kann oft ganz weggelassen werden, wenn nur ein Bild gezeigt werden soll. Ghostscript malt die meisten Dinge auf den Bildschirm, ohne sie zu benötigen showpage.

¡    161   showpage

[ Das funktioniert eigentlich nicht. Ghostscript gibt mir undefinedund syntaxerrorfür diese Token. Vielleicht gibt es einen Modus, den ich aktivieren muss. ]

Luser Droog
quelle
Vielleicht liegt es an diesem Programm. Der Online-Verdichter mag es auch nicht.
Luser Droog
1

Ändern Sie die negativen Rollen in positive

Negative Rollen können immer in positive Rollen geändert werden .

3 -1 roll
3 2 roll

5 -2 roll
5 3 roll
Luser Droog
quelle
Was ist effizienter 3 -1 rolloder 3 2 roll? In meinem mentalen Modell sollte das erstere effizienter sein, da es nur einen Schritt dauert. Ist mein mentales Modell korrekt?
Küsse meine Achsel
Ehrlich gesagt bin ich mir nicht sicher. Hier ist meine Implementierung , bei der alle negativen Rollen als erster Schritt in positive umgewandelt werden. Ich denke, dass es immer noch mindestens 2 Züge (3. Wert nach oben , 3 Werte nach unten ) mit einer Flat-Array-Implementierung des Stapels erfordern würde . Mein Stack ist jedoch segmentiert, sodass der Zugriff über Funktionsaufrufe erfolgt, um die Segmente zu verwalten. Es gibt also mit Sicherheit effizientere Implementierungsmöglichkeiten als ich. ... Eine Sache, die ich jetzt sehe: Ich sollte außerhalb der Schleifen auf Stackunderflow prüfen .
Luser Droog
1
Die Quelldatei ist seit meinem letzten Kommentar verschoben worden. Hier ist meine Implementierung des rollOperators.
Luser Droog
0

Benutze meine G-Bibliothek

https://github.com/luser-dr00g/G

Es ist eine Textdatei. Keine Erweiterung, um die kürzest mögliche Syntax zu laden.

Es erlaubt dieses 203-Zeichen-Sierpinksi-Dreieck-Programm

[48(0-1+0+1-0)49(11)43(+)45(-)/s{dup
0 eq{exch{[48{1 0 rlineto}49 1 index
43{240 rotate}45{120 rotate}>>exch
get exec}forall}{exch{load
exch 1 sub s}forall}ifelse 1 add}>>begin
9 9 moveto(0-1-1)9 s fill

umzuschreiben in 151 Bytes als

3(G)run $
{A - B + A + B - A}
{B B}

{A - B - B}7{[ex{du w{(>K?\2u)$}if}fora]}rep
cvx[/A{3 0 rl}/B 1 in/-{120 rot}/+{-120 rot}>>b
100 200(k?B9)$ showp

Arbeitsdatei mit Kommentaren

Durch die Verwendung der Funktion für abgekürzte Systemnamen 1(G)runentfällt die Belastung durch lange Bedienernamen vollständig. Ein Bedienername muss nur lang genug sein, um ihn von den anderen zu unterscheiden.

So

  • add wird ad
  • mul wird mu
  • index wird i
  • usw. usw.

Verwenden Sie den PLRM- Anhang F für die Standardtabelle der Bedienernamen.

Die Funktion von Operator Strings ist auch dann verfügbar, wenn die abgekürzten Namen nicht ausgewählt sind. Die nackte Bibliothek hat eine "Basisebene", die durch einfaches Hinzufügen (G)runohne weitere Dekorationen ausgewählt wird.

Die Basisebene enthält eine neue Funktion, .die den Integer-Code für einen Operator akzeptiert (derselbe oben erwähnte Anhang F) und ihn ausführt.

Die neue Funktion $durchläuft eine Zeichenfolge und ruft .jede auf. Der ASCII-Code wählt den Bediener also direkt nach seiner Nummer aus.

Mit einer neuen Funktion @können Sie bis zum Ende der Tabelle in Anhang F vordringen, indem Sie das Leerzeichen (ASCII 0x20) als 0 behandeln.

Mit einer neuen Funktion #können Sie weiter in die Tabelle vordringen, indem Sie zuerst 95 (0x5F) hinzufügen, sodass das Leerzeichen 0x20 als 127 (0x7F) behandelt wird, der nächste Code nach dem letzten druckbaren ASCII-Zeichen ~126 (0x7E).

Mit zwei neuen Funktionen !können Sie auf eine tief verschachtelte Struktur von Arrays und / oder Dikten mit einem Indexarray von Indizes / Schlüsseln zugreifen , statt auf langwierige Ausdrücke vieler get(und put) Operatoren.

(G)run 7 Zeichen kauft das Basislevel.

1(G)run 8 Zeichen kaufen diese UND abgekürzten Systemnamen.

3(G)run $Mit 9 Zeichen beginnt eine implizite Prozedur sofort damit, die Quellzeilen bis zur nächsten leeren Zeile abzusuchen und die erste Zeile als aufgerufene Prozedur A, die nächste Zeile als aufgerufene Prozedur Busw. zu definieren . Dadurch sollten die meisten defzum Definieren erforderlichen s entfernt werden viele Dinge, ohne sie in ein Wörterbuch einwickeln zu müssen oder ihnen sogar explizit Namen zu geben.

Luser Droog
quelle