在Postscript中打高尔夫球的秘诀?


Answers:


3

嵌入式解码器

Postscript程序具有独特的(?)功能,可以将自己的程序文本读取为数据。这通常用于通过image其接收操作者的数据采集-程序作为输入,并且这个过程通常会使用currentfile随后readlinereadstring,或readhexstring。但是换一种说法,image它只是另一个循环运算符,因此任何循环都可以预读。一个示例是《绿皮书》中的行式打印机模拟器。

使用token运算符可在文件或字符串上调用扫描程序,并以数字或空格(或其他方式:请参见其他答案)分隔名称。

PS中的一个简单的PS解释器:

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

二进制运算符字符串解码器

由于我似乎无法获得原始的二进制令牌来工作(请参阅其他答案),因此我利用“嵌入式解码”的思想来利用二进制令牌机制将代码打包为8位字符串,然后即时处理和分析字符串中的命令。

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

.过程从堆栈中获取一个数字,并将其作为两个字节的字符串中的第二个字节插入,第一个字节是二进制令牌的前缀字节,并指定了可执行系统名称。我们使用扫描程序的规则在十六进制字符串中保存一个字节,即在十六进制字符串中奇数个半字节用额外的0个半字节填充,因此3个十六进制半字节会生成一个2字节的字符串。然后将该字符串标记为可执行,并调用该字符串以exec调用扫描程序,生成所需的可执行系统名称,然后加载该名称并执行运算符。在$栈上的一个字符串的每个字节执行此,使用.过程两次,一次是作为循环体,然后执行该循环操作者forall通过数字。

更紧凑地说,这些过程如下所示:

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

因此,55个字符购买二进制令牌字符串。或者,对于6个字符(如果使用空格终止,则为7个字符),则可以加载G库(G)run用于定义.$上面的代码(以及其他一些代码,以扩展可到达ascii的代码的范围)。

在我的填字游戏中进一步说明了答案


1
实际上,如果后记为编码形式,则这需要您在比较内容时格外小心。具体来说,如果您正在查看运算符,则必须将其解析为令牌,然后将令牌的名称与目标值进行比较。
AJMansfield

2

当生成图形输出和控制台输出无关紧要时,请使用=代替pop


2

用ASCII85替换十六进制字符串

可能是旧消息,但我才刚学到。:)

您可以使用Postscript解释器与编码过滤器和剪切和粘贴交互地进行操作。但我将展示如何使用dc “手动”它。

因此,这是一个十六进制字符串。我们将其分成4个字节的块。

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

接通dc,我们将它们输入为32位(无符号)big-endian-byte-order数字。然后使用mod -off以85为基数的数字(直到为0之前,应该为5)。

0>直流
16i
95206ED8
爱
d85%n85 /
82
d85%n85 /
83
d85%n85 /
82
d85%n85 /
78
d85%n85 /
47
d85%n85 /
0                    

用填充最后一个块将00 00产生(十进制),而忽略了我们填充的相同字节数。

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

加33进入ASCII和poof的可打印范围!ASCII 85。

80 111 115 116 115 99 114 105 112 116 58 105 115 58 102 35 86 63
解码为:Postscript:is:f#V?%%%糟糕!应该说“有趣”!我搞砸了。:)

将其包装在<~...中~>,并且Level-2 Postscript可以访问8位数据,该价格比hex便宜。


2

这是一个快速工具:包裹多个定义[...>>begin以消除关键字def(nb。[与相同<<)。

 def def
[>>begin

所以请记住:不止两个... 聚集在一起;)


规则不应该是“两个以上”吗?/a 1 def/b 2 def/c 3 def与比较<</a 1/b 2/c 3>>begin。我们需要更多空间来进行防御。
Thomas W.

哇。我没有那个。是的,计算需要改进。
luser droog 2014年

实际上,这应该是[/a 1/b 2/c 3>>begin
Thomas W.

脸部手掌 。。。
luser droog 2014年

1
如果您要命名以自定界标记终止的内容,则可能不适用。在/a{pop 2 mul}def或者\b[2 3]def,将def成本只有3个字符,而不是4
AJMansfield

2

虽然大多数后记运营商在语法标识符(因此必须与空间(或其他方式)分隔),名称[]<<,和>>是自定界和扫描仪会检测到它们中间没有空间。出于同样的原因,你不能引用这些名称与通常的/literal语法(如:/[是两个符号:一个空的名称等同于文本()cvn cvlit和可执行文件的名称[等同于([)cvn cvx exec)。

为了重新定义这些名称不能提及的名称,我们可以使用在字典中用作键时隐式转换为名称的字符串(方便!)。

此示例说明了滥用这些运算符来执行算术。

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

而且<<[(和mark)都是同一件事。


我自己的Postscript解释器xpost,也提供了一些正确的右括号。讨论区


2
另外,/结束前一个令牌,因此您不需要在其之前有空格。
杰夫·里迪

1

排除重复使用长运算符名称的情况

如果您已经在使用<<>>begin字典,则/?{}每次重新定义都会有4个字符的固定开销。因此,长度为n的操作符重复N次将产生
(4 + n)-(N *(n -1))个字符计数变化。

将此公式设置为0可以得出收支平衡点的方程。由此我们可以针对每个变量求解另一个变量,得出
n =-(N -4)/(1- N)和
N =(4 + n)/(n -1)。

不,我们无法回答诸如“缩写多少次值得使用?”的问题。n = 5,所以N = 9/4。登顶吧,因为您无法有效地调用print的1/4倍。因此,3。3用途。的确,

print print print
/P{print}p p p

(当然,假设您已经支付了<<>>begin激活定义的开销)。

当然,二进制令牌可以解决这种问题,从系统名称表中为您提供前255个2字节的名称:0x92、0x ??。二进制令牌也是自定界的,因为第一个字节的高位不在ASCII范围内,所以之前或之后不需要空格。


1

二进制代币

对于PostScript程序的最终压缩,最终的边界是二进制令牌,它使您可以完全删除长运算符名称,而不再需要使用ASCII干净程序。

因此,从压缩后的代码块开始

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

我们在PLRM的背面查找所有名称(附录F,第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

然后以146(十进制)字节为前缀键入它们。vim帮助输入任意字节

然后在vim中,可以直接键入压缩文件,因此:

[/ T [70 {R 0 ^V146 ^V133} 48 {} 49 {} 43 {A ^V146 ^V136} 45 {A ^V146 ^V110 ^V146 ^V136} 91 { ^V146 ^V30 ^V146 ^V57 [ ^V/。[146 ^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^V 100 ^V146 ^V63}^V146^V73 57} { 62^V 146^V^V146 ^V62 { ^V146 ^V100 ^V146^V

...您必须在此处输入空格以终止 ^V -62并开始1,但您可以备份并稍后将其删除...

1 ^V146 ^V1S} ^V146 ^V73} ^V146^V 85

...必须在此处输入空格以终止 ^V -85并开始1,但您可以备份并稍后将其删除...

1 ^V146 ^V169} >> ^V146 ^V13 ^V146^V 107

... 3位数代码的第3位数终止字节输入,因此以下 0内容很正常,很方便...

0秒^V146^V 167

在屏幕上(在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>§

如果只是为了显示图片,通常可以完全忽略这一点。Ghostscript不需要将大多数内容绘制到屏幕上showpage

¡    161   showpage

[ 这实际上不起作用。Ghostscript在给我undefinedsyntaxerror这些令牌。也许我需要启用某些模式]


也许与这个程序有关。该在线压实不喜欢它,无论是。
luser droog

1

将负数更改为正数

向总是可以更改为正向

3 -1 roll
3 2 roll

5 -2 roll
5 3 roll

这是更有效的3 -1 roll还是3 2 roll?在我的心智模型中,前者应该更有效,因为它只需要一招。我的心理模型正确吗?
亲吻我的腋窝2014年

老实说,我不确定。这是我的实现,第一步是将所有负向滚动转换为正向。我认为使用平面数组实现堆栈至少还需要2次移动(将第3个值向上移动,将3个值向下移动)。但是我的堆栈是分段的,因此访问通过函数调用来管理分段;因此肯定有比我更有效的实施方法。...我现在看到的一件事:我应该在循环之外检查stackunderflow 。
luser droog 2014年

1
自从我上次评论以来,源文件已移动。这是我roll运算符的实现
luser droog 2015年

0

使用我的G库

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

这是一个文本文件。无扩展名,以最短的语法加载它。

它允许这个203个字符的Sierpinksi Triangle程序

[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

改写为151字节为

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

带注释的工作文件

使用缩写的系统名称功能, 1(G)run完全消除了冗长的操作员名称的负担。操作员名称仅需足够长即可将其与其他名称区分开。

所以

  • add 变成 ad
  • mul 变成 mu
  • index 变成 i
  • 等等等

使用PLRM附录F用于操作员名称的标准表。

即使未选择缩写名称,操作符字符串的功能仍然可用。裸库通过简单地添加来选择“基础级别”(G)run而无需其他修饰。

基本级别包括一个新函数.,该函数接受运算符的整数代码(与上述相同的附录F)并执行它。

新函数$遍历一个字符串并调用.每个字符串。因此,ascii代码直接通过数字选择运算符。

@通过将空格字符(Ascii 0x20)视为0,可以使用新功能深入附录F中表格的底部。

通过使用新功能#,您可以通过首先添加95(0x5F)进一步深入表中,以便将char 0x20空间视为127(0x7F),这是最后一个可打印的ASCII字符~126(0x7E)之后的下一个代码。

两个新功能!使您可以使用索引/键的索引数组访问数组和/或dict的深度嵌套结构,而不是许多get(和put)运算符的乏味表达式。

(G)run 7个字符购买基本级别。

1(G)run 8个字符购买该AND的缩写系统名称。

3(G)run $9个字符立即开始隐式过程块扫描源代码行,直到下一行空行,并将第一行定义为一个过程称为A,将下一行定义为一个过程称为B,以此类推。这应删除def用于定义的大多数s很多东西,而无需将它们包装在字典中,甚至无需明确为其命名。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.