Wskazówki dotyczące gry w golfa w Postscript?


Odpowiedzi:


3

Wbudowany dekoder

Program Postscript ma unikalną (?) Możliwość odczytu własnego tekstu programu jako danych. Jest to zwykle wykorzystywany przez imageoperatora, który odbiera dane pozyskania PROCEDURA jako wejście, a procedura ta wykorzystuje często currentfilenastępuje readline, readstringlub readhexstring. Ale patrząc z innej strony, imagejest to po prostu inny operator pętli, więc każda pętla może czytać dalej . Przykładem jest emulator drukarki liniowej z zielonej księgi.

Użycie tokenoperatora wywołuje skaner na pliku lub ciągu, pobierając numer lub spację (lub w inny sposób: patrz inna odpowiedź).

Prosty interpreter PS w PS:

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

Dekoder ciągu binarnego operatora

Ponieważ wydaje się, że nie mogę uzyskać nieprzetworzonych tokenów binarnych dla mnie (patrz inna odpowiedź), skorzystałem z idei „osadzonego dekodowania”, aby wykorzystać mechanizm binarnych tokenów do spakowania kodu w ciągi 8-bitowe, a następnie w locie manipuluj i analizuj polecenia z łańcucha .

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

.Procedura trwa kilka ze stosu i umieszcza go jako drugi bajt w ciąg dwóch bajtów, pierwszy bajt jest prefiks-bajtowy dla binarnego tokena, określając wykonywalnego nazwę systemu. Zapisujemy bajt w łańcuchu szesnastkowym, stosując regułę skanera, że ​​nieparzysta liczba skórek w łańcuchu szesnastkowym jest uzupełniana dodatkowym skrobakiem 0, więc 3 szesnastkowe szczypce dają łańcuch 2-bajtowy. Łańcuch jest następnie oznaczany jako wykonywalny i wywoływany za pomocą execktórego wywołuje skaner, tworzy żądaną nazwę systemu wykonywalnego, a następnie ładuje nazwę i wykonuje operator. $Ma to na każdy bajt z ciągiem w stos, za pomocą .procedury dwukrotnie , raz jako ciała pętli, a następnie wykonać operatora pętli forallliczby.

Bardziej kompaktowo, te procedury wyglądają następująco:

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

55 znaków kupuje ciągi binarnych tokenów. Lub, w przypadku 6 (być może 7, jeśli ją rozwiązać ze spacją) znaków, można załadować biblioteki G z (G)runktóry definiuje .i $jak powyżej (+ kilka innych, aby rozszerzyć zakres kodów ASCII osiągalny).

Dalej zilustrowane w mojej krzyżówce .


1
W rzeczywistości, jeśli PostScript jest w postaci zakodowanej, wymaga to szczególnej ostrożności przy porównywaniu rzeczy. W szczególności, jeśli szukasz na operatorów, to należy przeanalizować go jako żeton, a następnie porównać tokenu nazwy do wartości docelowej.
AJMansfield

2

Podczas generowania danych graficznych i danych wyjściowych konsoli nie ma znaczenia, użyj =zamiast pop.


2

Zamień szesnastki na ASCII85

Prawdopodobnie stare wiadomości, ale właśnie się tego nauczyłem. :)

Możesz to zrobić za pomocą interpretera postscriptowego interaktywnie z filtrem kodującym i metodą wycinania i wklejania. Ale pokażę, jak używaćdc to zrobić „ręcznie”.

Oto ciąg szesnastkowy. Podzieliliśmy go na 4-bajtowe fragmenty.

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

Rozpalając dc, wprowadzamy je jako 32-bitowe (niepodpisane) liczby porządkowe typu big-endian-byte. Następnie mod -off base-85 cyfr (powinno być 5, aż dojdziesz do 0).

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

Wypełnianie ostatniego fragmentu 00 00, daje (dziesiętnie), pomijając tę ​​samą liczbę bajtów, którą wypełniliśmy.

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

Dodaj 33, aby przejść do zakresu drukowanego ASCII i pufa! ASCII85.

80 111 115 116 115 99 114 105 112 116 58 105 115 58 102 35 86 63
który dekoduje do: Postscript: is: f # V? %%% Ups! powinien powiedzieć „zabawa”! Gdzieś spieprzyłem. :)

Zawiń to w <~... ~>, a Postscript poziomu 2 może uzyskać dostęp do 8-bitowych danych, tańszych niż hex.


2

Oto szybki: zawiń wiele definicji, [...>>beginaby wyeliminować słowo kluczowe def(nb. [To to samo, co <<).

 def def
[>>begin

Więc pamiętaj: więcej niżtrzydwa ... stado razem ! ;)


Czy reguła nie powinna być „więcej niż dwa”? Porównaj /a 1 def/b 2 def/c 3 defz <</a 1/b 2/c 3>>begin. Potrzebujemy więcej miejsca na def.
Thomas W.

Łał. Nie myślałem o tym. Tak, obliczenia wymagają poprawy.
luser droog

Właściwie to powinno być[/a 1/b 2/c 3>>begin
Thomas W.

twarz-dłoń. . . .
luser droog

1
Może to nie mieć zastosowania, jeśli nazywasz coś, co kończy się na tokenie samoograniczającym. W /a{pop 2 mul}deflub \b[2 3]defThe defkosztuje tylko 3 znaki, a nie 4.
AJMansfield

2

Chociaż większość operatorów PostScript są składniowo identyfikatory (a zatem musi być czasoprzestrzeni (lub otherwise-) rozdzielany), nazwy [, ], <<, i >>są samo-ograniczającej i skaner wykryje je bez interwencji miejsca. Z tego samego powodu nie można odwoływać się do tych nazw w zwykłej /literalskładni (np. /[To dwa tokeny: pusta dosłowna nazwa równoważna ()cvn cvliti nazwa wykonywalna [równoważna ([)cvn cvx exec).

Aby ponownie zdefiniować te nazwy, których nie można wymienić po nazwie, możemy użyć ciągów, które są domyślnie konwertowane na nazwy, gdy są używane jako klucze w słowniku (wygodne!).

Ten przykład ilustruje nadużywanie tych operatorów do wykonywania arytmetyki.

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

Również <<i [(i mark) wszystkie oznaczają to samo.


Mój własny interpreter postscriptowy, xpost , udostępnia również nawias klamrowy z pewnymi ograniczeniami. dyskusja


2
Również /kończy poprzedni znak, więc nie potrzebują miejsca przed nim.
Geoff Reedy

1

Odrzuć wielokrotne użycie długich nazw operatorów

Jeśli korzystasz już ze <<>>beginsłownika, /?{}na redefinicję nakładają się 4 znaki. Zatem operator długości n powtarzanych N razy da zmianę liczby znaków
(4 + n ) - ( N * ( n - 1)).

Ustawienie tego wzoru równą 0 daje równanie przerwania nawet punkt. Na tej podstawie możemy rozwiązać dla każdej zmiennej drugą wartość, uzyskując
n = - ( N - 4) / (1 - N ) i
N = (4 + n ) / ( n - 1).

Nie, możemy odpowiedzieć na pytania typu: „Dla ilu zastosowań„ druku ”warto skracać?” n = 5, więc N = 9/4. Podejmij sufit, ponieważ nie możesz skutecznie wywołać drukowania 1/4 razy. Tak więc, 3 3 zastosowania. I rzeczywiście

print print print
/P{print}p p p

( <<>>beginoczywiście przy założeniu, że zapłaciłeś już za aktywację definicji).

Oczywiście tokeny binarne robią tego rodzaju dyskusję, dając pierwsze 255 nazw z systemowej tabeli nazw jako 2 bajty: 0x92, 0x ??. A tokeny binarne są również samoograniczające, nie wymagają spacji przed ani po, ponieważ wysoki bit pierwszego bajtu jest poza zakresem ascii.


1

Tokeny binarne

Dla ostateczności zip-up programu PostScript że ostateczna granica jest binarne tokeny , które pozwala na usunięcie długie nazwy operatora całkowicie, kosztem nie mając programu ASCII-czyste.

A więc zaczynając od zwartego bloku kodu postscriptowego

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

Sprawdzamy wszystkie nazwiska z tyłu PLRM (załącznik F, s. 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

A następnie wpisz je w prefiksie 146 bajtem (dziesiętnym). vim pomoc przy wprowadzaniu dowolnych bajtów

Następnie w vimie skondensowany plik można wpisać bezpośrednio, więc:

[/ 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 73 146 57} {^V 100 ^V146 ^V63}^V146 146 62 { 146 100 146 62^V^V^V^V^V^V^V^V^V

... musisz wpisać tutaj spację, aby zakończyć ^V-62 i rozpocząć 1, ale możesz później wykonać kopię zapasową i usunąć ...

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

... musisz wpisać tutaj spację, aby zakończyć ^V-85 i rozpocząć 1, ale możesz później wykonać kopię zapasową i usunąć ...

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

... trzecia cyfra 3-cyfrowego kodu kończy wprowadzanie bajtów, więc poniższe 0jest normalne, wygodnie ...

0 S ^V146 ^V167

Który będzie wyglądał tak na ekranie (w vimie):

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

Tego można często całkowicie pominąć, jeśli celem jest tylko pokazanie obrazu. Ghostscript maluje większość rzeczy na ekranie bez potrzebyshowpage .

¡    161   showpage

[ To nie działa. Ghostscript daje mi undefinedi syntaxerrorza te tokeny. Może jest jakiś tryb, który muszę włączyć. ]


Może to coś w tym programie. Ziemnych w Internecie to nie podoba, albo.
luser droog

1

Zmień negatywne rzuty na pozytywne

Rzuty ujemne zawsze można zmienić na rzuty dodatnie .

3 -1 roll
3 2 roll

5 -2 roll
5 3 roll

Który jest bardziej wydajny 3 -1 rolllub 3 2 roll? W moim modelu mentalnym ten pierwszy powinien być bardziej wydajny, ponieważ wymaga tylko jednego ruchu. Czy mój model mentalny jest poprawny?
pocałuj mnie pod pachą

Szczerze mówiąc, nie jestem pewien. Oto moja implementacja , w której wszystkie negatywne rzuty są konwertowane na pozytywne jako pierwszy krok. Myślę, że nadal wymagałoby to co najmniej 2 ruchów (przesunięcie trzeciej wartości w górę , przesunięcie 3 wartości w dół ) przy zastosowaniu płaskiej tablicy stosu. Ale mój stos jest podzielony na segmenty, więc dostęp przechodzi przez wywołania funkcji w celu zarządzania segmentami; więc są z pewnością bardziej wydajne sposoby wdrażania niż ja. ... Teraz widzę jedno: powinienem sprawdzić przepływ stackunder poza pętlami.
luser droog

1
Plik źródłowy został przeniesiony od czasu mojego ostatniego komentarza. Oto moja realizacja tego rolloperatora.
luser droog

0

Użyj mojej biblioteki G.

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

To plik tekstowy. Bez rozszerzenia, aby załadować możliwie najkrótszą składnię.

Umożliwia ten 203-znakowy program Trójkąta Sierpinksi

[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

do przepisania w 151 bajtach jako

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

plik roboczy z komentarzami

Korzystanie ze skróconej nazwy systemu 1(G)runcałkowicie eliminuje ciężar długich nazw operatorów. Nazwa operatora musi być wystarczająco długa, aby odróżnić ją od innych.

Więc

  • add staje się ad
  • mul staje się mu
  • index staje się i
  • itd itd.

Użyj dodatku F dla PLRM, aby uzyskać standardową tabelę nazw operatorów.

A funkcja Ciągów operatora jest dostępna, nawet jeśli nie wybrano skrótów. W nagiej bibliotece wybrano „poziom podstawowy”, dodając po prostu(G)run bez dalszych dekoracji.

Poziom podstawowy zawiera nową funkcję, .która akceptuje kod liczby całkowitej dla operatora (ten sam dodatek F wspomniany powyżej) i wykonuje go.

Nowa funkcja $dokonuje iteracji po łańcuchu i wywołuje .każdą z nich. Zatem kod ascii bezpośrednio wybiera operatora według numeru.

Nowa funkcja @pozwala dotrzeć do dołu tabeli w załączniku F, traktując znak spacji (Ascii 0x20) jako 0.

Nowa funkcja #pozwala dotrzeć do dalszej tabeli, najpierw dodając 95 (0x5F), więc znak spacji 0x20 jest traktowany jako 127 (0x7F), następny kod po ostatnim drukowanym znaku ascii ~126 (0x7E).

Dwie nowe funkcje !umożliwiają dostęp do głęboko zagnieżdżonej struktury tablic i / lub nagrań za pomocą tablicy indeksów indeksów / kluczy, zamiast żmudnych wyrażeń wielu get(i put) operatorów.

(G)run 7 znaków kupuje poziom podstawowy.

1(G)run 8 znaków kupuje ORAZ skrócone nazwy systemów.

3(G)run $9 znaków natychmiast rozpoczyna linie źródła skanowania bloku procedury niejawnej aż do następnej pustej linii, a zdefiniowanie pierwszego wiersza jako wywołanej procedury A, następny wiersz jest zdefiniowany jako wywołana procedura Bitp. Powinno to usunąć większość elementów defpotrzebnych do zdefiniowania wiele rzeczy, bez konieczności zawijania ich w słowniku, ani nawet jawnego nadawania im nazw.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.