Consejos para jugar al golf en PostScript?

14

Como uno de los idiomas menos populares, es difícil encontrar literatura sobre la vanguardia de la piratería de postscript. Entonces, ¿qué descubrimientos han hecho los golfistas aquí para explotar el modelo de pila (u otras características) para superar la verbosidad inherente de Postscript?

luser droog
fuente
Encontré algunas páginas externas: sites.google.com/site/codegolfingtips/Postscript
luser droog

Respuestas:

3

Decodificador Integrado

Un programa Postscript tiene una capacidad única (?) Para leer su propio texto de programa como datos. Esto se usa normalmente por el imageoperador que recibe una adquisición-procedimiento de datos como entrada, y este procedimiento utiliza a menudo currentfileseguido por readline, readstringo readhexstring. Pero visto de otra manera, imagees solo otro operador de bucle, por lo que cualquier bucle puede seguir leyendo . Un ejemplo es el emulador de impresora de líneas del Libro Verde.

El uso del tokenoperador invoca el escáner en un archivo o cadena, extrayendo un número o espacio (o de lo contrario: ver otra respuesta) nombre delimitado.

Un simple intérprete de PS en PS:

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

Decodificador de cadena de operador binario

Ya que parece que no puede obtener primas símbolos binarios a trabajar para mí (ver otra respuesta), he hecho uso de la "incrustados decodificar" la idea de explotar el mecanismo símbolo binario para empacar código en cadenas de 8 bits, y luego manipular y analizar los comandos de la cadena sobre la marcha .

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

El .procedimiento toma un número de la pila y lo inserta como el segundo byte en una cadena de dos bytes, siendo el primer byte el prefijo-byte para un token binario, especificando un nombre de sistema ejecutable. Guardamos un byte en la cadena hexagonal mediante el uso de una regla del escáner de que un número impar de mordiscos en la cadena hexagonal se rellena con un mordisco 0 adicional, por lo que 3 mordiscos hexagonales producen una cadena de 2 bytes. La cadena se marca como ejecutable y se llama con lo execque invoca el escáner, produce el nombre del sistema ejecutable deseado, y luego carga el nombre y ejecuta el operador. El $hace esto en cada byte de una cadena en la pila, usando el .procedimiento dos veces , una vez como el cuerpo del bucle, y luego para ejecutar el operador de bucle forallpor número.

De manera más compacta, estos procedimientos se ven así:

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

Entonces, 55 caracteres compra cadenas de fichas binarias. O, para los caracteres 6 (quizás 7, si lo termina con un espacio), puede cargar la biblioteca G con la (G)runque se define .y $como arriba (+ algunos otros para ampliar el rango de códigos ascii-alcanzables).

Más ilustrado en mi crucigrama respuesta .

luser droog
fuente
1
En realidad, si la posdata está en forma codificada, entonces esto requiere que tenga mucho cuidado al comparar cosas. Específicamente, si está buscando operadores, debe analizarlo como un token y luego comparar el nombre del token con el valor objetivo.
AJMansfield
2

Cuando la salida gráfica y la salida de la consola no importen, use en =lugar de pop.

Thomas W.
fuente
2

Reemplace las cadenas hexagonales con ASCII85

Probablemente viejas noticias, pero acabo de enterarme. :)

Puede hacerlo utilizando el intérprete PostScript de forma interactiva con un filtro de codificación y cortar y pegar. Pero voy a mostrar cómo usarlo dc"a mano".

Entonces, aquí hay una cadena hexadecimal. Lo dividimos en trozos de 4 bytes.

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

Encendiendo dc, los ingresamos como números de orden de bytes grandes de 32 bits (sin signo). Luego mod -off base-85 dígitos (debe haber 5 hasta llegar a 0).

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

Rellenando el último fragmento con 00 00, produce (decimal), omitiendo el mismo número de bytes que rellenamos.

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

¡Agregue 33 para cambiar al rango imprimible de ASCII y poof! ASCII85.

80 111 115 116 115 99 114 105 112 116 58 105 115 58 102 35 86 63
que decodifica a: Postdata: es: f # V? %%% ¡Vaya! debería decir 'diversión'! La cagué en alguna parte. :)

Envuélvalo en <~... ~>, y Postscript de nivel 2 puede acceder a datos de 8 bits, más barato que el hexadecimal.

luser droog
fuente
2

Aquí hay un resumen: envuelva varias definiciones [...>>beginpara eliminar la palabra clave def(nb. [Es lo mismo que <<).

 def def
[>>begin

Así que recuerda: más queTresdos ... ¡ acuden juntos ! ;)

luser droog
fuente
¿No debería ser la regla "más de dos"? Comparar /a 1 def/b 2 def/c 3 defcon <</a 1/b 2/c 3>>begin. Necesitamos más espacios para def.
Thomas W.
Guau. No había pensado en eso. Sí, el cálculo necesita mejoras.
luser droog
En realidad, esto debería ser[/a 1/b 2/c 3>>begin
Thomas W.
cara de palma. . . .
luser droog
1
Esto podría no aplicarse si está nombrando algo que termina en un token autodelimitante. En /a{pop 2 mul}defo \b[2 3]def, el defúnico cuesta 3 caracteres, no 4.
AJMansfield
2

Aunque la mayoría de los operadores de PostScript son sintácticamente identificadores (y por lo tanto debe ser espacio-(u otros métodos) delimitada), los nombres [, ], <<, y >>son auto-delimitación y escáner detectará sin espacio intermedio. Por la misma razón, no puede hacer referencia a estos nombres con la /literalsintaxis habitual (por ejemplo, /[son dos tokens: un nombre literal vacío equivalente a ()cvn cvlit, y el nombre ejecutable [equivalente a ([)cvn cvx exec).

Para redefinir estos nombres, que no se pueden mencionar por nombre, podemos usar cadenas que se convierten implícitamente en nombres cuando se usan como claves en un diccionario (¡conveniente!).

Este ejemplo ilustra el abuso de estos operadores para realizar operaciones aritméticas.

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

Además <<y [(y mark) todos significan lo mismo.


Mi propio intérprete de postscript, xpost , también hace que la llave derecha esté disponible con algunas restricciones. discusión

luser droog
fuente
2
Además, /finaliza el token anterior para que no necesite un espacio antes.
Geoff Reedy
1

Factorizar usos repetidos de nombres de operador largos

Si ya está utilizando un <<>>begindiccionario, hay una sobrecarga constante de /?{}4 caracteres por redefinición. Por lo tanto, un operador de longitud n repetido N veces producirá un cambio de recuento de caracteres de
(4 + n ) - ( N * ( n - 1)).

Establecer esta fórmula igual a 0 da la ecuación del punto de equilibrio . De esto podemos resolver para cada variable en términos de la otra, produciendo
n = - ( N - 4) / (1 - N ) y
N = (4 + n ) / ( n - 1).

No, podemos responder preguntas como: "¿Para cuántos usos de 'imprimir' vale la pena abreviar?" n = 5, entonces N = 9/4. Tome el techo, ya que no puede llamar efectivamente a imprimir 1/4 veces. Entonces, 3. 3 usos. Y de hecho,

print print print
/P{print}p p p

(suponiendo que ya ha pagado los gastos generales <<>>beginpara activar la definición, por supuesto).

Por supuesto, los tokens binarios hacen que este tipo de discusión sea discutible, dándote los primeros 255 nombres de la tabla de nombres del sistema como 2 bytes: 0x92, 0x ??. Y los tokens binarios también se auto delimitan, no requieren espacios en blanco antes o después, ya que el bit alto del primer byte está fuera del rango de ascii.

luser droog
fuente
1

Fichas Binarias

Para el último zip-up de un programa PostScript, esa frontera final son tokens binarios que le permiten eliminar completamente nombres largos de operadores, a costa de no tener un programa ASCII-clean.

Comenzando con un bloque compactado de código 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

Buscamos todos los nombres en la parte posterior del PLRM (Apéndice 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

Y luego escríbalos con el prefijo un 146byte (decimal). vim ayuda para ingresar bytes arbitrarios

Luego, en vim, el archivo condensado se puede escribir directamente, por lo que:

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

... debe ingresar un espacio aquí para terminar el ^V-62 e iniciar el 1, pero puede hacer una copia de seguridad y eliminarlo más tarde ...

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

... tiene que ingresar un espacio aquí para terminar el ^V-85 e iniciar el 1, pero puede hacer una copia de seguridad y eliminarlo más tarde ...

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

... el tercer dígito del código de 3 dígitos termina la entrada de bytes, por lo que lo siguiente 0aquí es normal, convenientemente ...

0 S ^V146 ^V167

Que se verá así en la pantalla (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>§

Este a menudo se puede omitir por completo si el objetivo es solo mostrar una imagen. Ghostscript pinta la mayoría de las cosas en la pantalla sin necesidad showpage.

¡    161   showpage

[ Esto en realidad no está funcionando. Ghostscript me está dando undefinedy syntaxerrorpor estas fichas. Tal vez hay algún modo que necesito habilitar. ]

luser droog
fuente
Tal vez sea algo sobre este programa. Al compactador en línea tampoco le gusta.
luser droog
1

Cambiar rollos negativos a positivos

Los rollos negativos siempre se pueden cambiar a rollos positivos .

3 -1 roll
3 2 roll

5 -2 roll
5 3 roll
luser droog
fuente
¿Cuál es más eficiente 3 -1 rollo 3 2 roll? En mi modelo mental, el primero debería ser más eficiente porque solo toma un movimiento. ¿Es correcto mi modelo mental?
besar mi axila
Honestamente, no estoy seguro. Aquí está mi implementación , donde todos los rollos negativos se convierten en positivos como primer paso. Creo que aún requeriría al menos 2 movimientos (mover el tercer valor hacia arriba , mover 3 valores hacia abajo ) con una implementación de matriz plana de la pila. Pero mi pila está segmentada, por lo que el acceso pasa por llamadas a funciones para administrar los segmentos; así que hay formas más eficientes de implementar que yo. ... Una cosa que veo ahora: debería verificar el flujo de la pila fuera de los bucles.
luser droog
1
El archivo fuente se ha movido desde mi último comentario. Aquí está mi implementación del rolloperador.
luser droog
0

Usa mi biblioteca G

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

Es un archivo de texto. Sin extensión, para la sintaxis más corta posible para cargarlo.

Permite este programa Sierpinksi Triangle de 203 caracteres

[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

para ser reescrito en 151 bytes como

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

archivo de trabajo con comentarios

El uso de la función abreviada de nombres de sistema 1(G)runelimina por completo la carga de los nombres largos de operadores. El nombre de un operador solo debe ser lo suficientemente largo como para distinguirlo de los demás.

Entonces

  • add se convierte ad
  • mul se convierte mu
  • index se convierte i
  • etcétera etcétera.

Use el Apéndice F de PLRM para la tabla estándar de nombres de operadores.

Y la función de Operator Strings está disponible incluso si los nombres abreviados no están seleccionados. La biblioteca básica tiene un "nivel base" seleccionado al agregar simplemente (G)runsin más decoraciones.

El nivel base incluye una nueva función .que acepta el código entero para un operador (el mismo Apéndice F mencionado anteriormente) y lo ejecuta.

La nueva función $itera a través de una cadena y llama .a cada una. Entonces, el código ASCII selecciona directamente al operador por número.

Una nueva función le @permite llegar al final de la tabla en el Apéndice F al tratar el carácter de espacio (Ascii 0x20) como 0.

Una nueva función le #permite llegar más arriba en la tabla agregando primero 95 (0x5F) para que el espacio char 0x20 se trate como 127 (0x7F), el siguiente código después del último carácter ascii imprimible ~126 (0x7E).

Dos nuevas funciones le !permiten acceder a una estructura profundamente anidada de matrices y / o dictados con una matriz de índices / claves, en lugar de expresiones tediosas de muchos get(y put) operadores.

(G)run 7 caracteres compra el nivel base.

1(G)run 8 caracteres compra eso y abrevia los nombres del sistema.

3(G)run $9 caracteres comienza inmediatamente un bloque de procedimiento implícito explorando líneas de origen hasta la siguiente línea en blanco, y definiendo la primera línea como un procedimiento llamado A, la siguiente línea se define como un procedimiento llamado B, etc. Esto debería eliminar la mayoría de los defs necesarios para definir muchas cosas, sin necesidad de envolverlas en un diccionario, ni siquiera darles nombres explícitamente.

luser droog
fuente