人気の低い言語の1つとして、ポストスクリプトハッカーの前衛に関する文献を見つけることは困難です。では、ここでゴルファーは、スタックモデル(またはその他の機能)を活用してPostscript固有の冗長性を克服するためにどのような発見をしましたか?
人気の低い言語の1つとして、ポストスクリプトハッカーの前衛に関する文献を見つけることは困難です。では、ここでゴルファーは、スタックモデル(またはその他の機能)を活用してPostscript固有の冗長性を克服するためにどのような発見をしましたか?
回答:
Postscriptプログラムには、独自のプログラムテキストをデータとして読み取るユニークな機能があります。これは、通常で使用されるimage
受信オペレータデータ収集・プロシージャを入力として、この手順は、しばしば使用しcurrentfile
、続いてreadline
、readstring
またはreadhexstring
。しかし、別の方法で見られるのimage
は、別のループ演算子であるため、どのループも先読みできます。例としては、Green Bookのラインプリンターエミュレーターがあります。
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
この.
プロシージャは、スタックから数値を取得し、それを2バイト文字列の2番目のバイトとして挿入します。最初のバイトは、実行可能なシステム名を指定するバイナリトークンのプレフィックスバイトです。スキャナーのルールを使用して、16進文字列の奇数個のニブルに余分な0ニブルを埋め込むというスキャナーのルールを使用して、16進文字列にバイトを保存します。文字列はマークされ、実行可能として呼び出さexec
スキャナれる呼び出し、所望の実行可能なシステム名を生成し、次いで、名前をロードし、オペレータを実行します。これ$
は、スタック上の文字列の各バイトでこれを行い、.
プロシージャを2回使用し、1回はループ本体として使用し、その後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到達可能なコードの範囲を拡張するために+数など)上記のように。
クロスワードパズルの答えでさらに説明します。
おそらく古いニュースですが、私はそれを学びました。:)
エンコードフィルターとカットアンドペーストを使用して、インタラクティブにpostscriptインタープリターを使用して実行できます。しかし、私は使用方法を示しますdc
、「手動」でそれを行う。
だから、ここに16進文字列があります。これを4バイトのチャンクに分割しました。
95 20 6e d8 d0 59 49 35 50 74 ba c5 08 2d
dcを起動して、これらを32ビット(符号なし)ビッグエンディアンバイトオーダー数として入力します。それからmod -off base-85 digits(0になるまで5になるはずです)。
0> dc 16i 95206ED8 愛 d85%n85 / 82 d85%n85 / 83 d85%n85 / 82 d85%n85 / 78 d85%n85 / 47 d85%n85 / 0
最後のチャンクを00 00
でパディングすると、(10進数)が得られ、パディングしたのと同じバイト数が省略されます。
47 78 82 83 82 66 81 72 79 83 25 72 82 25 69 2 53 30 [2 53]
33を追加して、ASCIIとpoofの印刷可能範囲にシフトします!ASCII85。
80 111 115 116 115 99 114 105 112 116 58 105 115 58 102 35 86 63
デコード:Postscript:is:f#V?%%%おっとっと!「楽しい」と言うべきです!どこかで台無しにした。:)
<~
... ~>
でラップすると、レベル2のPostscriptは16進数よりも安価な8ビットデータにアクセスできます。
[...>>begin
キーワードを削除するために、複数の定義をラップしますdef
(nb。はと[
同じです<<
)。
def def
[>>begin
覚えておいてください: 以上三二... 一緒に群がる!;)
/a 1 def/b 2 def/c 3 def
してください<</a 1/b 2/c 3>>begin
。defにはさらにスペースが必要です。
[/a 1/b 2/c 3>>begin
/a{pop 2 mul}def
または\b[2 3]def
、def
唯一の3つの文字、ない4.コスト
ほとんどのPostScriptオペレータが構文的識別子(したがって、省スペース(またはotherwise-でなければなりません)区切り)、名前ですが[
、]
、<<
、および>>
自己区切りされ、スキャナはスペースを介在せずにそれらを検出します。同じ理由で、通常の/literal
構文ではこれらの名前を参照できません(たとえば/[
、2つのトークン:に相当する空のリテラル名()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
)はすべて同じことを意味します。
/
前のトークンを終了するので、その前にスペースは必要ありません。
既に<<>>begin
辞書を使用している場合、/?{}
再定義ごとに4文字のオーバーヘッドが常に発生します。したがって、N回繰り返される長さnの演算子は、
(4 + n)-(N *(n -1))の文字カウントの変更をもたらします。
この式を0に設定すると、損益分岐点の式が得られます。これから、各変数を他の変数に関して解くと、
n =-(N -4)/(1- N)および
N =(4 + n)/(n -1)が得られます。
いいえ、「 'print'を何回使用するのか、短縮する価値はありますか?」といった質問に答えることはできません。n = 5、したがってN = 9/4。効果的にprintを1/4回呼び出すことはできないため、天井を守ってください。したがって、3。3を使用します。本当に、
print print print
/P{print}p p p
(<<>>begin
もちろん、定義をアクティブにするためのオーバーヘッドを既に支払っていると仮定します)。
もちろん、バイナリトークンはこの種の意味をなくし、システム名テーブルの最初の255個の名前を2バイト(0x92、0x ??)として提供します。また、バイナリトークンも自己区切りであり、最初のバイトの上位ビットがASCIIの範囲外にあるため、前後に空白を必要としません。
最終的なフロンティアがバイナリトークンである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、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
そして、146
(10進数の)バイトを接頭辞として入力します。任意のバイトを入力するための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 ^V100 ^V146^V 63}^V146^V73 ^V146 ^V57} { ^V146 ^V62 { ^V146 ^V100 ^V146 ^V62
... ^V
-62 を終了して1を開始するには、ここにスペースを入力する必要がありますが、後でバックアップして削除できます...
1 ^V146 ^V1S} ^V146 ^V73} ^V146 ^V85
... ^V
-85 を終了して1を開始するには、ここにスペースを入力する必要がありますが、後でバックアップして削除できます...
1 ^V146 ^V169} >> ^V146 ^V13 ^V146 ^V107
... 3桁のコードの3桁目はバイト入力を終了させるため、0
ここでの次は正常で便利です...
0 S ^V146 ^V167
画面上で次のように表示されます(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が私を与えているundefined
とsyntaxerror
、これらのトークンのために。おそらく、有効にする必要のあるモードがあります。]
3 -1 roll
か3 2 roll
?私のメンタルモデルでは、前者の方が効率的である必要があります。私のメンタルモデルは正しいですか?
roll
。
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)の次のコードになります。
2つの新しい関数!
により、多くの(および)演算子の退屈な表現ではなく、インデックス/キーのインデックス配列を使用して、深くネストされた配列および/または辞書の構造にアクセスできます。get
put
(G)run
7文字で基本レベルが購入されます。
1(G)run
8文字で購入し、システム名を省略します。
3(G)run $
9文字は、暗黙的な手続きブロックをすぐに開始し、次の空白行までソース行をスキャンし、最初の行をという手順としてA
定義しますB
。次の行は、という手順として定義されます。これにより、def
定義に必要なs辞書でラップする必要もなく、明示的に名前を付ける必要もなく、たくさんのものがあります。