Postscript에서의 골프 팁?


14

덜 대중적인 언어 중 하나로서 포스트 스크립트 해커의 전위에 관한 문헌을 찾기가 어렵습니다. 그렇다면 골퍼들은 Postscript의 고유 한 세부 표현을 극복하기 위해 스택 모델 (또는 다른 기능)을 활용하기 위해 어떤 발견을 했습니까?


외부 페이지를 찾았습니다. sites.google.com/site/codegolfingtips/Postscript
luser droog

답변:


3

임베디드 디코더

포스트 스크립트 프로그램에는 고유 한 (?) 기능이있어 자체 프로그램 텍스트를 데이터로 읽을 수 있습니다. 이는 일반적으로 사용되는 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 바이트 스트링 스택와 두번째 바이트로서 삽입을 행 번호를 얻어, 실행 시스템의 이름을 지정하는 바이너리 토큰 프리픽스 바이트, 상기 제 1 바이트. 16 진수 문자열의 홀수 개의 니블이 추가 0 니블로 채워지는 스캐너 규칙을 사용하여 16 진 문자열의 바이트를 저장하므로 3 개의 16 진 니블은 2 바이트 문자열을 생성합니다. 그런 다음 문자열을 실행 파일 로 표시 하고 exec호출하여 스캐너를 호출하고 원하는 실행 시스템 이름을 생성 한 다음 이름을로드하고 연산자를 실행합니다. 은 $은 USING, 스택 스트링의 각 바이트에이를 수행 .절차를 두번 루핑 조작을 실행 한 후 일단 루프 체로서, 및 forall숫자로.

보다 간결하게,이 절차는 다음과 같습니다.

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

따라서 55 문자는 이진 토큰 문자열을 구입합니다. 또는 문자 (당신이 공간을 종료하면 아마 7) 6, 당신은로드 할 수 있습니다에 대한 G 라이브러리(G)run있는 정의 .$상기와 (몇 가지 다른 + 아스키-도달 코드의 범위를 확장하기 위해).

크로스 워드 퍼즐 답변 에 더 자세히 설명되어 있습니다.


1
실제로, 포스트 스크립트가 인코딩 된 형식 인 경우, 내용을 비교할 때 특히주의해야합니다. 당신이 운영자보고있는 경우 특히, 당신은 해야한다 토큰으로 구문 분석 한 후 토큰의 비교 이름을 목표 값으로.
AJMansfield

2

그래픽 출력을 생성하고 콘솔 출력은 중요하지 않습니다 때 사용하는 =대신 pop.


2

16 진 문자열을 ASCII85로 교체

아마 오래된 뉴스이지만 방금 배웠습니다. :)

인코딩 필터 및 잘라 내기 및 붙여 넣기와 함께 대화식으로 포스트 스크립트 인터프리터를 사용하여 수행 할 수 있습니다. 그러나 dc"손으로" 하는 방법을 보여 드리겠습니다 .

여기에 16 진 문자열이 있습니다. 우리는 이것을 4 바이트 청크로 나눕니다.

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

dc를 시작하면 32 비트 (부호없는) 빅 엔디안 바이트 순서 번호로 입력합니다. 그런 다음 mod -off base-85 자리수 (0이 될 때까지 5가 있어야 함).

0> dc
16i
95206
일체 포함
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]

인쇄 가능한 ASCII 및 of의 범위로 이동하려면 33을 추가하십시오! 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. 데프에 더 많은 공간이 필요합니다.
Thomas W.

와. 나는 그렇지 않았다. 예, 계산이 개선되어야합니다.
luser droog

실제로, 이것은 다음과 같아야합니다[/a 1/b 2/c 3>>begin
Thomas W.

얼굴-팜. . . .
luser droog

1
자체 구분 토큰에서 종료되는 이름을 지정하는 경우에는 적용되지 않을 수 있습니다. 에서 /a{pop 2 mul}def또는 \b[2 3]def의는 def단지 3 자,하지 4. 비용
AJMansfield

2

대부분의 포스트 스크립트 연산자 구문 식별자 (따라서 공간 - (또는 otherwise-이어야 함)로 구분), 이름이지만 [, ], <<, 및 >>자기 구분하고 스캐너 공간을 개입없이 감지합니다. 같은 이유로 일반적인 /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) 모두 같은 일을 의미한다.


나만의 포스트 스크립트 인터프리터 인 xpost 도 약간의 제한이 있지만 올바른 중괄호를 사용할 수 있습니다. 토론


2
또한 /이전 토큰을 끝내므로 공백이 필요하지 않습니다.
Geoff Reedy

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입니다. 인쇄를 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 [{R 70 0 ^V146 ^V133 48} {49} {43} {A ^V146 ^V136} {A 45 ^V146 ^V110 ^V146 ^V136} 91 { ^V146 ^V30^V 146 ^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 ^V100 ^V146 ^V63}^V146 ^V73 ^V146 ^V57} { ^V146 ^V62 { ^V146 ^V100 ^V146 ^V62

...- ^V62 를 끝내고 1을 시작하려면 여기에 공백을 입력해야 하지만 나중에 백업하고 삭제할 수 있습니다 ...

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

...- ^V85 를 끝내고 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

[ 이것은 실제로 작동하지 않습니다. 고스트 스크립트는 나에게주고 undefined그리고 syntaxerror이러한 토큰. 어쩌면 몇 가지있다 모드 나 활성화해야합니다. ]


어쩌면 그것은 뭔가에 관한 것입니다. 프로그램 . 온라인 압축기는 하나, 그것을 좋아하지 않는다.
luser droog

1

네거티브 롤을 포지티브로 변경

네거티브 롤은 항상 포지티브 롤로 변경할 수 있습니다 .

3 -1 roll
3 2 roll

5 -2 roll
5 3 roll

어느 것이 더 효율적입니다 3 -1 roll또는 3 2 roll? 내 정신 모델에서 전자는 한 번의 이동만으로 더 효율적이어야합니다. 내 정신 모델이 맞습니까?
내 겨드랑이에 키스

솔직히 확실하지 않습니다. 다음 은 모든 네거티브 롤이 첫 번째 단계로 양수로 변환되는 구현 입니다. 스택의 평면 배열 구현으로 여전히 적어도 2 번의 이동 (3 번째 값 위로 이동, 3 값 아래 로 이동)이 필요하다고 생각합니다 . 그러나 내 스택은 세그먼트 화되므로 액세스는 세그먼트를 관리하기 위해 함수 호출을 거칩니다. 그래서 내가 한 것보다 확실히 효율적인 방법이 있습니다. ... 지금 내가 볼 한 가지는 : 루프 외부 에서 stackunderflow를 확인해야합니다 .
luser droog

1
마지막 주석 이후 소스 파일이 이동했습니다. 다음roll연산자의 구현 입니다 .
luser droog

0

내 G 라이브러리 사용

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

텍스트 파일입니다. 가장 짧은 구문을로드 할 수있는 확장명이 없습니다.

203-char 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은 마지막 인쇄 가능한 ASCII 문자 ~126 (0x7E) 다음에 나오는 다음 코드 인 127 (0x7F)로 처리됩니다 .

두 개의 새로운 함수를 !사용하면 많은 (및 ) 연산자 의 지루한 표현이 아니라 인덱스 / 키 의 인덱스 배열 을 사용하여 깊게 중첩 된 배열 및 / 또는 dicts 구조에 액세스 할 수 있습니다 .getput

(G)run 7자는 기본 레벨을 구매합니다.

1(G)run 8 문자는 그와 약어 시스템 이름을 구입합니다.

3(G)run $9 문자는 즉시 다음 빈 줄까지 소스 행을 스캔 하고 암시 적 절차 블록을 시작 하고 첫 번째 줄을라는 절차 A로 정의하고 다음 줄은이라는 절차로 정의합니다 B. 이렇게하면 def정의하는 데 필요한 대부분이 제거됩니다. 사전에 그것들을 감싸거나 이름을 명시하지 않아도 많은 것들.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.