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.