Postscriptでのゴルフのヒント


14

人気の低い言語の1つとして、ポストスクリプトハッカーの前衛に関する文献を見つけることは困難です。では、ここでゴルファーは、スタックモデル(またはその他の機能)を活用してPostscript固有の冗長性を克服するためにどのような発見をしましたか?


外部ページをいくつか見つけました:sites.google.com/site/codegolfingtips/Postscript
luser droog

回答:


3

組み込みデコーダー

Postscriptプログラムには、独自のプログラムテキストをデータとして読み取るユニークな機能があります。これは、通常で使用されるimage受信オペレータデータ収集・プロシージャを入力として、この手順は、しばしば使用しcurrentfile、続いてreadlinereadstringまたは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到達可能なコードの範囲を拡張するために+数など)上記のように。

クロスワードパズルの答えでさらに説明します


1
実際、ポストスクリプトがエンコードされた形式である場合、これはあなたがものを比較するのに特別な注意が必要です。具体的には、演算子を見ている場合は、それをトークンとして解析し、トークンの名前をターゲット値と比較する必要あります。
-AJMansfield

2

グラフィカル出力とコンソール出力の生成が重要でない場合は、の=代わりに使用しますpop


2

16進文字列をASCII85に置き換えます

おそらく古いニュースですが、私はそれを学びました。:)

エンコードフィルターとカットアンドペーストを使用して、インタラクティブに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ビットデータにアクセスできます。


2

[...>>beginキーワードを削除するために、複数の定義をラップしますdef(nb。はと[同じです<<)。

 def def
[>>begin

覚えておいてください: 以上二... 一緒に群がる;)


ルールを「2つ以上」にすべきではありませんか?と比較/a 1 def/b 2 def/c 3 defしてください<</a 1/b 2/c 3>>begin。defにはさらにスペースが必要です。
トーマスW. 14年

ワオ。私はそれをしていませんでした。はい、計算には改善が必要です。
luserはドローグ

実際、これは次のようになります[/a 1/b 2/c 3>>begin
トーマスW.

フェイスパーム。。。。
luserはドローグ

1
自己区切りトークンで終了する名前を付けている場合、これは適用されない場合があります。では/a{pop 2 mul}defまたは\b[2 3]defdef唯一の3つの文字、ない4.コスト
AJMansfield

2

ほとんどの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)はすべて同じことを意味します。


私自身のポストスクリプトインタープリターxpostも、いくつかの制限付きで右中括弧を使用可能にします。討論


2
また、/前のトークンを終了するので、その前にスペースは必要ありません。
ジェフリーディー

1

長い演算子名の繰り返し使用の因数分解

既に<<>>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の範囲外にあるため、前後に空白を必要としません。


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、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が私を与えているundefinedsyntaxerror、これらのトークンのために。おそらく、有効にする必要のあるモードがあります]


多分それはこのプログラムについての何かです。オンライン圧縮機はどちらか、それが好きではありません。
luser droog

1

負のロールを正に変更する

負のロールは、常に正のロールに変更できます。

3 -1 roll
3 2 roll

5 -2 roll
5 3 roll

どちらがより効率的です3 -1 roll3 2 roll?私のメンタルモデルでは、前者の方が効率的である必要があります。私のメンタルモデルは正しいですか?
私の脇の下にキス

正直なところ、わかりません。これが私の実装です。最初のステップとしてすべての負のロールが正に変換されます。スタックのフラット配列実装では、少なくとも2回の移動(3番目の値をに移動、3つの値をに移動)が必要だと思います。しかし、私のスタックはセグメント化されているため、アクセスは関数呼び出しを経てセグメントを管理します。確かに、私がやったよりも効率的な実装方法があります。...私が今見ている1つのこと:ループの外側でstackunderflowをチェックする必要があります。
luserはドローグ

1
ソースファイルは、前回のコメントから移動しました。オペレーターの私の実装ですroll
luser droog

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)の次のコードになります。

2つの新しい関数!により、多くの(および)演算子の退屈な表現ではなく、インデックス/キーのインデックス配列を使用して、深くネストされた配列および/または辞書の構造にアクセスできます。getput

(G)run 7文字で基本レベルが購入されます。

1(G)run 8文字で購入し、システム名を省略します。

3(G)run $9文字は、暗黙的な手続きブロックをすぐに開始し、次の空白行までソース行をスキャンし、最初の行をという手順としてA定義しますB。次の行は、という手順として定義されます。これにより、def定義に必要なs辞書でラップする必要もなく、明示的に名前を付ける必要もなく、たくさんのものがあります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.