问题
Much of the code-golf played on Stack Overflow involved ASCII figures, and codegolf.SE is shaping up the same way.
That's all well and good, but I'd like to inject a little variety into the output, and have written my recent question Output a playable crossword grid explicitly to encourage graphical format entries.
To show willing, I've started trying to hack together a reference implementation in postscript. Alas, I'm completely new to the language, and am having trouble doing basic file IO. Does anyone know of a basic resource that covers the topic?
I am aware of
file
(%stdin)
token
readstring
readline
but I am pretty unclear on exactly what the last three return and how one manipulates it after you've got it.
I have figured out that several return a <value> <boolean>
pair, and that I can use if
to test the boolean. Then what?
回答1:
A good book about postscript is "thinking in postscript" by Gleen Reid. You used to be able to get the book free for personal use from his website, but its down. You can still download it from http://replay.waybackmachine.org/20090621100720/http://www.rightbrain.com/pages/books.html
Chapter 14 covers file io and has sample code.
回答2:
There are some tricks and shortcuts to File IO in postscript that often aren't mentioned in the introductory sections of postscript books. The best-kept secret among them is the 'token' operator. It allows you to borrow the interpreter's own scanner to read a bareword (producing a nametype object), a number (producing an integertype or realtype object), or an array of these (but only if you use the executable-array notation, ie. curly braces, otherwise you just get a marktype object which represents the left square bracket).
As a simple example, I often use this one-liner to embed an extended commentary section in a postscript program:
%!
{ currentfile token pop /END-COMMENT eq {exit} if } loop
Each word here is read from the current file and converted
into a name object and compared to "/END-COMMENT".
It can have but does not need a preceeding slash.
This program produces an image of a snowman in a blizzard,
suitable for use as stationary.
END-COMMENT
showpage
Edit: I realize now that this is a really terrible answer ... so far.
My example above is little more than:
(Some
multi-line
text)
pop
So, for real this time.
To start with, you need a loop
.
{
} loop
An inside this loop, you read some data.
/src (datafile) (r) file def
{
src readline
} loop
As you observed, the file-reading operators all return a boolean on the top of the stack. Perfect!
/src (datafile) (r) file def
{
src readline {
}{
exit
} ifelse
} loop
So a boolean true will do something and keep looping, but a boolean false (no-more-data) causes the loop to terminate. But readline
needs a string to put its data into. And it returns the string even in the false case.
/src (datafile) (r) file def
/str 80 string def
{
src str readline {
processline
}{
pop exit
} ifelse
} loop
Now we have a string containing a line (80 chars max) of data from the file. If it resembles Postscript syntax at all, you can use token
in a loop (like my first example; it reads each word from the file, converts it to an Executable Name and then compares it to a Literal Name; if the names match, exit the loop). Or you can use get
in a loop (or the forall
loop) to extract each byte as an integer. But the cool thing to do is search
.
This procedure scans a string for space-delimited words and prints each one to stdout on its own line.
/processline { % (line)
( ) { % (line) ( )
search { % (post) ( ) (pre)
=
}{ % (no-match)
=
exit
} ifelse
} loop
} def
It's usually easier just to duplicate cases here, when you want to do the same thing to the last word as all the others. You can do some tricky stuff to process the topmost string and then test the boolean; but it's not really worth the hassle. The cool thing about search
is the convenient order in which it returns its results. It puts the beginning of the string on top so you can work with it. Then it gives you the "match" (which is the same as the "seek" you gave initially (although it's referenced to the working string now)) and the remainder; and they're already in the correct order for the next go 'round the loop.
So what do you want to do with your extracted, space-delimited words read from an external file?
If they represent numbers, you can cvi
or cvr
them and do arithmetic. If they're alphanumeric, you can print
or show
them; or cvn
them and use them as atomic symbols.
回答3:
It sounds like you want to convert a simple text file (representing a crossword puzzle layout) to PostScript code that draws that crossword puzzle on a page. While it is true that PostScript is Turing-complete, and thus can be programmed to do this, I think programming the whole thing in PostScript is not a good approach.
PostScript is a tricky language to program in, especially if you want to mess around with arbitrary data. When I want PostScript output, I start by hacking together some sample PostScript that draws what I want, with all the parameters hardcoded. Then I write code in some other language that outputs that PostScript code, varying the appropriate parts as needed based on the input.
I see that doesn't read very well, so here's an example. If I have an input file that contains the radius of a circle, then I'll write a program in Python that reads that radius, then writes PostScript code to draw the circle, with the radius hardcoded in the PostScript.
I started working on a crossword puzzle project years ago. Didn't get too far, but the attached PostScript code will draw a small crossword puzzle. Note that "proper" PostScript follows some document conventions that I didn't bother with; I have some sketchy notes about them that I can post.
This PostScript code does do some calculations. Generally I'll write functions in PostScript for things I expect to do a lot, or for things that need data that only the PostScript interpreter will have, e.g., the width of a string in the current font, for centering.
I haven't cleaned this up any. Feel free to ask for clarifications. HTH.
%!
% "Example"
% by (author)
% Generated 16 Apr 2005 22:19 by (program name)
% should really be eps
% unit conversions
/inch {72 mul} bind def
/cm {inch 2.54 div} bind def
%%%%%% start of adjustable parameters
% these may be fiddled with, within reason
% be careful not to change the syntax
% paper size
/page_width 8.5 inch def
/page_height 11 inch def
% page margins
/top_margin 1 inch def
/left_margin 1 inch def
% how many "cells" wide and high
/puzzle_width 9 def
/puzzle_height 9 def
% cell_size: height and width of a one-letter square, in points
/cell_size .25 inch def
% line width, in points. 1 is about maximum.
/line_width .375 def
% font and size for the numbers
/cell_font {/Helvetica 5} bind def
% top/left margin for numbers within a cell
/num_margin 1.5 def
%%%%%% end of adjustable parameters
<< /PageSize [page_width page_height] >> setpagedevice
/w_pts puzzle_width cell_size mul def
/h_pts puzzle_height cell_size mul def
/ulx left_margin def
/uly page_height top_margin sub def
/lrx ulx w_pts add def
/lry uly h_pts sub def
% draw grid
line_width setlinewidth
0 setgray
newpath ulx uly w_pts h_pts neg rectstroke
lry cell_size uly {
newpath ulx exch moveto w_pts 0 rlineto stroke
} for
ulx cell_size lrx {
newpath lry moveto 0 h_pts rlineto stroke
} for
% fill in black spaces
% ulx uly width height blackrect
% all in terms of cells; upper-left-most is 0,0
/blackrect
{
<< >> begin
/h exch def /w exch def /y exch def /x exch def
newpath
ulx x cell_size mul add
uly y cell_size mul sub
w cell_size mul
h cell_size mul neg
rectfill
end
} bind def
0 setgray
0 0 1 1 blackrect
3 0 2 1 blackrect
8 0 1 1 blackrect
4 1 1 1 blackrect
2 2 1 1 blackrect
6 2 1 1 blackrect
8 3 1 1 blackrect
0 4 2 1 blackrect
7 4 2 1 blackrect
0 5 1 1 blackrect
2 6 1 1 blackrect
6 6 1 1 blackrect
4 7 1 1 blackrect
0 8 1 1 blackrect
4 8 2 1 blackrect
8 8 1 1 blackrect
% draw numbers
% x y h s drawnum
% x and y in terms of cells; upper-left-most is 0,0. s is string.
% h is height of numbers. should never change, so compute once before any calls.
/drawnum
{
<< >> begin
/s exch def /h exch def /y exch def /x exch def
newpath
ulx x cell_size mul add num_margin add
uly y cell_size mul sub num_margin sub h sub
moveto s show
end
} bind def
0 setgray
cell_font selectfont
% compute font height
mark
newpath 0 0 moveto
(0) false charpath flattenpath pathbbox
/fh exch def
cleartomark newpath
1 0 fh (1) drawnum
2 0 fh (2) drawnum
5 0 fh (3) drawnum
6 0 fh (4) drawnum
7 0 fh (5) drawnum
0 1 fh (6) drawnum
3 1 fh (7) drawnum
5 1 fh (8) drawnum
8 1 fh (9) drawnum
0 2 fh (10) drawnum
3 2 fh (11) drawnum
4 2 fh (12) drawnum
7 2 fh (13) drawnum
0 3 fh (14) drawnum
2 3 fh (15) drawnum
6 3 fh (16) drawnum
2 4 fh (17) drawnum
1 5 fh (18) drawnum
7 5 fh (19) drawnum
8 5 fh (20) drawnum
0 6 fh (21) drawnum
3 6 fh (22) drawnum
7 6 fh (23) drawnum
0 7 fh (24) drawnum
2 7 fh (25) drawnum
5 7 fh (26) drawnum
6 7 fh (27) drawnum
1 8 fh (28) drawnum
6 8 fh (29) drawnum
showpage
来源:https://stackoverflow.com/questions/5136164/is-there-a-file-io-in-postscript-for-dummies