Conseils pour jouer au golf en Postscript?


14

En tant que l'une des langues les moins populaires, il est difficile de trouver de la littérature sur l'avant-garde du piratage postscript. Alors, quelles découvertes les golfeurs ont-ils fait pour exploiter le modèle de pile (ou d'autres fonctionnalités) pour surmonter la verbosité inhérente à Postscript?


Réponses:


3

Décodeur intégré

Un programme Postscript a une capacité unique (?) De lire son propre texte de programme sous forme de données. Ceci est normalement utilisé par l' imageopérateur qui reçoit un procédure d'acquisition des données en entrée, et cette procédure utilise souvent currentfilesuivie readline, readstringou readhexstring. Mais vu d'une autre manière, ce imagen'est qu'un autre opérateur de bouclage, de sorte que n'importe quelle boucle peut être lue à l'avance . Un exemple est l'émulateur d'imprimante ligne du Livre vert.

L'utilisation de l' tokenopérateur appelle le scanner sur un fichier ou une chaîne, en tirant un nombre ou un espace (ou autre): voir autre réponse) nom délimité.

Un simple interprète PS dans PS:

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

Décodeur de chaîne d'opérateur binaire

Puisque je n'arrive pas à me faire cru les jetons binaires (voir autre réponse), j'ai utilisé l'idée de "décodage intégré" pour exploiter le mécanisme des jetons binaires pour emballer le code dans des chaînes de 8 bits, puis manipuler et analyser les commandes de la chaîne à la volée .

/.{
    <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

La .procédure prend un nombre dans la pile et l'insère comme deuxième octet dans une chaîne de deux octets, le premier octet étant le préfixe-octet d'un jeton binaire, spécifiant un nom de système exécutable. Nous enregistrons un octet dans la chaîne hexadécimale en utilisant une règle du scanner selon laquelle un nombre impair de quartets dans la chaîne hexagonale est complété par un quartet supplémentaire, donc 3 quartets hexagonaux produisent une chaîne de 2 octets. La chaîne est ensuite marquée comme exécutable et appelée avec execlaquelle invoque le scanner, produit le nom du système exécutable souhaité, puis charge le nom et exécute l'opérateur. Le $fait cela sur chaque octet d'une chaîne de la pile, en utilisant le. procédure deux fois , une fois comme corps de boucle, puis pour exécuter l'opérateur de bouclage forallpar numéro.

De façon plus compacte, ces procédures ressemblent à ceci:

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

Ainsi, 55 caractères achètent des chaînes de jetons binaires. Ou, pour 6 (peut-être 7, si vous le terminez par un espace), vous pouvez charger la bibliothèque G avec(G)run laquelle définit .et $comme ci-dessus (+ quelques autres pour étendre la gamme de codes accessibles par ascii).

Encore illustré dans ma réponse de puzzle de mots croisés .


1
En fait, si le postscript est sous forme codée, cela nécessite que vous fassiez très attention à comparer les choses. Plus précisément, si vous recherchez des opérateurs, vous devez l' analyser en tant que jeton, puis comparer le nom du jeton à la valeur cible.
AJMansfield

2

Lorsque la génération d'une sortie graphique et d'une sortie console n'a pas d'importance, utilisez =plutôt pop.


2

Remplacer les chaînes hexadécimales par ASCII85

Probablement de vieilles nouvelles, mais je viens de l'apprendre. :)

Vous pouvez le faire en utilisant l'interpréteur postscript de manière interactive avec un filtre d'encodage et un copier-coller. Mais je vais montrer comment utiliserdc pour le faire "à la main".

Alors, voici une chaîne hexadécimale. Nous l'avons divisé en morceaux de 4 octets.

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

En activant le courant continu, nous les entrons sous forme de nombres d'ordre de gros octets de 32 bits (non signés). Ensuite mod base-85 -off chiffres (il devrait y avoir 5 jusqu'à ce que vous obtenez à 0).

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

Remplir le dernier morceau avec 00 00, donne (décimal), en omettant le même nombre d'octets que nous avons rempli.

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

Ajoutez 33 pour passer à la gamme imprimable d'ASCII et de pouf! ASCII85.

80111115116115 99114105112116 58105115 58102 35 86 63
qui décode en: Post-scriptum: est: f # V? %%%Oups! devrait dire «amusant»! J'ai foiré quelque part. :)

Enveloppez-le dans <~... ~>, et Postscript de niveau 2 peut accéder à des données 8 bits, moins cher que hexadécimal.


2

Voici un quickie: encapsulez plusieurs définitions [...>>beginpour éliminer le mot-clé def(nb. [Est le même que <<).

 def def
[>>begin

Alors souviens-toi: plus deTroisdeux ... affluent ensemble ! ;)


La règle ne devrait-elle pas être «plus de deux»? Comparez /a 1 def/b 2 def/c 3 defavec <</a 1/b 2/c 3>>begin. Nous avons besoin de plus d'espace pour def.
Thomas W.

Sensationnel. Je n'avais pas pensé à ça. Oui, le calcul doit être amélioré.
luser droog

En fait, cela devrait être[/a 1/b 2/c 3>>begin
Thomas W.

face-paume. . . .
luser droog

1
Cela peut ne pas s'appliquer si vous nommez quelque chose qui se termine sur un jeton à délimitation automatique. Dans /a{pop 2 mul}defou \b[2 3]def, le defseul coûte 3 caractères, pas 4.
AJMansfield

2

Alors que la plupart des opérateurs postscript sont des identificateurs syntaxiques (et doivent donc être délimités par des espaces (ou autrement)), les noms [,] , <<et >>sont auto-délimitation et scanner les détecter sans espace intermédiaire. Pour la même raison, vous ne pouvez pas faire référence à ces noms avec la /literalsyntaxe habituelle (par exemple, il /[s'agit de deux jetons: un nom littéral vide équivalent à ()cvn cvlitet le nom exécutable [équivalent à ([)cvn cvx exec).

Afin de redéfinir ces noms, qui ne peuvent pas être mentionnés par leur nom, nous pouvons utiliser des chaînes qui sont implicitement converties en noms lorsqu'elles sont utilisées comme clés dans un dictionnaire (pratique!).

Cet exemple illustre l'utilisation abusive de ces opérateurs pour effectuer l'arithmétique.

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

Aussi << et [(et mark) signifient tous la même chose.


Mon propre interprète postscript, xpost , rend également l'accolade droite bouclée disponible avec certaines restrictions. discussion


2
En outre, /termine le jeton précédent afin que vous n'ayez pas besoin d'espace avant.
Geoff Reedy

1

Factoriser les utilisations répétées des noms d'opérateurs longs

Si vous utilisez déjà un <<>>begindictionnaire, il y a une surcharge constante de /?{}4 caractères par redéfinition. Ainsi, un opérateur de longueur n répété N fois donnera un changement de nombre de caractères de
(4 + n ) - ( N * ( n - 1)).

La définition de cette formule à 0 donne l'équation du seuil de rentabilité . De cela, nous pouvons résoudre pour chaque variable en termes de l'autre, donnant
n = - ( N - 4) / (1 - N ) et
N = (4 + n ) / ( n - 1).

Non, nous pouvons répondre à des questions telles que: "Pour combien d'utilisations de" l'impression "vaut-il la peine d'abréger?" n = 5, donc N = 9/4. Prenez le plafond, car vous ne pouvez pas appeler efficacement l'impression 1/4 fois. Donc, 3. 3 utilisations. Et en effet,

print print print
/P{print}p p p

(en supposant que vous avez déjà payé les frais généraux de <<>>begin pour activer la définition, bien sûr).

Bien sûr, les jetons binaires font ce genre de moot, vous donnant les 255 premiers noms de la table des noms de système en 2 octets: 0x92, 0x ??. Et les jetons binaires sont également auto-délimitants, ne nécessitant aucun espace avant ou après, car le bit haut du premier octet est en dehors de la plage ascii.


1

Jetons binaires

Pour le zip ultime d'un programme PostScript, cette dernière frontière est les jetons binaires qui vous permettent de supprimer complètement les noms d'opérateurs longs, au prix de ne plus avoir de programme ASCII-clean.

Donc, en commençant par un bloc compact de code postscript

[/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

Nous recherchons tous les noms au dos du PLRM (Annexe F, pp. 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

Et puis tapez-les en préfixe par un 146octet (décimal).aide vim pour la saisie d'octets arbitraires

Puis dans vim, le fichier condensé peut être tapé directement, donc:

[/ 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 107 ^V146 ^V30 ^V146 ^V57 ^V146 ^V57 ^V146 ^V13} >> / S { ^V146 ^V56 B ^V146 ^V61 {T ^V146 ^V13 ^V146 ^V62 { ^V146 ^V100 ^V 146 ^V63}^V146 ^V73 ^V146 ^V57} { ^V146 ^V62 { ^V146 ^V100 ^V146 ^V62

... vous devez entrer un espace ici pour terminer le ^V-62 et démarrer le 1, mais vous pouvez le sauvegarder et le supprimer plus tard ...

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

... devez entrer un espace ici pour terminer le ^V-85 et démarrer le 1, mais vous pouvez le sauvegarder et le supprimer plus tard ...

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

... Le 3e chiffre du code à 3 chiffres met fin à la saisie d'octets, ce qui suit 0est normal, commodément ...

0 S ^V146 ^V167

Qui ressemblera à ceci à l'écran (en 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>§

Celui-ci peut souvent être complètement omis si le but est simplement de montrer une image. Ghostscript peint la plupart des choses à l'écran sans avoir besoin showpage.

¡    161   showpage

[ Cela ne fonctionne pas vraiment. Ghostscript me donne undefinedet syntaxerrorpour ces jetons. Il y a peut-être un mode que je dois activer. ]


C'est peut-être quelque chose à propos de ce programme. Le compacteur en ligne ne l'aime pas non plus.
luser droog

1

Changer les rouleaux négatifs en positifs

Les rouleaux négatifs peuvent toujours être remplacés par des rouleaux positifs .

3 -1 roll
3 2 roll

5 -2 roll
5 3 roll

Quel est le plus efficace 3 -1 rollou 3 2 roll? Dans mon modèle mental, le premier devrait être plus efficace car il suffit d'un mouvement. Mon modèle mental est-il correct?
embrasse mon aisselle le

Honnêtement, je ne suis pas sûr. Voici mon implémentation , où tous les rouleaux négatifs sont convertis en positifs comme première étape. Je pense que cela nécessiterait encore au moins 2 mouvements (déplacer la 3e valeur vers le haut , déplacer 3 valeurs vers le bas ) avec une mise en œuvre à plat de la pile. Mais ma pile est segmentée, donc l'accès passe par des appels de fonction pour gérer les segments; il existe donc certainement des moyens de mise en œuvre plus efficaces que moi. ... Une chose que je vois maintenant: je devrais vérifier le stackunderflow en dehors des boucles.
luser droog

1
Le fichier source a bougé depuis mon dernier commentaire. Voici ma mise en œuvre de l' rollopérateur.
luser droog

0

Utiliser ma bibliothèque G

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

C'est un fichier texte. Aucune extension, pour la syntaxe la plus courte possible pour le charger.

Il permet ce programme Sierpinksi Triangle de 203 caractères

[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

à réécrire en 151 octets

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

fichier de travail avec commentaires

En utilisant la fonction de noms de système abrégés, 1(G)run élimine complètement le fardeau des noms d'opérateurs longs. Un nom d'opérateur n'a besoin que d'être assez long pour le distinguer des autres.

Donc

  • add devient ad
  • mul devient mu
  • index devient i
  • etc.

Utilisez le PLRM annexe F pour le tableau standard des noms d'opérateurs.

Et la fonction des chaînes d'opérateur est disponible même si les noms abrégés ne sont pas sélectionnés. La bibliothèque nue a un "niveau de base" sélectionné en ajoutant simplement(G)run sans autres décorations.

Le niveau de base comprend une nouvelle fonction .qui accepte le code entier pour un opérateur (le même appendice F mentionné ci-dessus) et l'exécute.

La nouvelle fonction $parcourt une chaîne et fait appel .à chacun. Le code ascii sélectionne donc directement l'opérateur par numéro.

Une nouvelle fonction @vous permet d'atteindre le bas du tableau de l'annexe F en traitant le caractère espace (Ascii 0x20) comme 0.

Une nouvelle fonction #vous permet d'accéder plus loin dans le tableau en ajoutant d'abord 95 (0x5F) pour que le caractère d'espace 0x20 soit traité comme 127 (0x7F), le code suivant après le dernier caractère ascii imprimable~ 126 (0x7E).

Deux nouvelles fonctions !vous permettent d'accéder à une structure profondément imbriquée de tableaux et / ou de dict avec un tableau d'index d'indices / clés, plutôt que des expressions fastidieuses de nombreux opérateurs get(et put).

(G)run 7 caractères achète le niveau de base.

1(G)run 8 caractères achètent les noms de système ET abrégés.

3(G)run $9 caractères commencent immédiatement un bloc de procédure implicite en balayant les lignes des sources jusqu'à la ligne vierge suivante, et en définissant la première ligne comme une procédure appelée A, la ligne suivante est définie comme une procédure appelée B, etc. Cela devrait supprimer la plupart des defs nécessaires pour définir beaucoup de choses, sans avoir besoin de les envelopper dans un dictionnaire, ni même de leur donner explicitement des noms.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.